aparar tailing - e adicioná-lo levando para números em shell

1

Eu tenho um arquivo CSV com números entre aspas duplas e sem aspas duplas e para alguns números. Eu tenho que corrigir os símbolos negativos para apenas números: o símbolo negativo final tem que ser removido e adicionado ao começo.

Entrada de amostra:

column 1, column 2, column 3, column 4, column 5
12-,"455,365.44-","string with quotes-and with a comma in between","4,432",6787

Exemplo de saída:

column 1, column 2, column 3, column 4, column 5
-12,"-455,365.44","string with quotes-and with a comma in between","4,432",6787
    
por Kiran 05.12.2017 / 21:33

5 respostas

1

Solução

GNU awk :

awk -v FPAT='[^,"]+|"[^"]+"' '
       NR==1; NR>1{ 
           for (i=1; i<=NF; i++) { 
               if ($i~/^"?[0-9]+([0-9,.]+[0-9]+)?-"?$/) { 
                   sub(/-/, "", $i); 
                   sub(/[0-9]/, "-&", $i); 
               } 
               printf "%s%s",$i,(i==NF? ORS:",") 
           } 
       }' file.csv
  • -v FPAT='[^,"]+|"[^"]+"' - padrão de regex que define o valor do campo
  • $i~/^"?[0-9]+([0-9,.]+[0-9]+)?-"?$/ - verifica se um campo contém um número com o sinal de menos à direita - (o número poderia ser aspas duplas)

A saída:

column 1, column 2, column 3, column 4, column 5
-12,"-455,365.44","string with quotes-and with a comma in between","4,432",6787
    
por 05.12.2017 / 23:24
0

Os dados de CSV exigem um analisador de CSV. Ruby tem um:

$ cat file.csv
12-,"455,365.44-","string with quotes-and with a comma in between","4,432",6787

$ ruby -rcsv -e '
    CSV.foreach(ARGV.shift) do |row|
        corrected = row.collect {|e| e.sub(/^([\d,.]+)-$/, "-\1")}
        puts CSV.generate_line(corrected)
    end
' file.csv
-12,"-455,365.44",string with quotes-and with a comma in between,"4,432",6787

O gerador de CSV decidiu que a "string com aspas" não precisava ser citada porque não contém vírgula.

    
por 05.12.2017 / 23:07
0

solução awk que não usa gawk FPAT:

NR==1;

NR > 1 {
    $0 = $0","

    while ($0) {
        match($0, / *"[^"]*" *,|[^,]*,/)
        f = substr($0,RSTART,RLENGTH-1)             # save what matched in f
        if (( f ~ /^"[0-9]([0-9,.]+[0-9]+)-"$/ ) ||
            ( f ~ /^[0-9]+[.]?[0-9]+-$/ ) ||
            ( f ~ /^[0-9]+-$/ )) {
            sub(/-/, "", f);
            sub(/[0-9]/, "-&", f);
        }
        $0 = substr($0, RLENGTH+1)                 
        printf "%s%s", f, (0 == NF ? "\n" : ",")
    }
}

A saída para o arquivo de amostra fornecido é:

column 1, column 2, column 3, column 4, column 5
-12,"-455,365.44","string with quotes-and with a comma in between","4,432",6787
    
por 06.12.2017 / 03:41
0

Com perl , mas não é adequado para todos os tipos de formatos csv , funciona para determinada amostra

$ perl -ne '@cols=/"[^"]+"|[^,]+/g;
            map {s/"(.*)-"$/"-$1"/ or s/(.*)-$/-$1/} @cols;
            print join ",", @cols' ip.csv
column 1, column 2, column 3, column 4, column 5
-12,"-455,365.44","string with quotes-and with a comma in between","4,432",6787
  • @cols=/"[^"]+"|[^,]+/g salvar campos na matriz
  • map {s/"(.*)-"$/"-$1"/ or s/(.*)-$/-$1/} @cols altera cada elemento da matriz. A segunda substituição é usada somente se primeiro não tiver sucesso
    • altere (.*) para ([\d,.]+) para restringir a correspondência a dígitos e , e . apenas. Isso ainda corresponderia a strings como ..,..-
  • print join ",", @cols imprime a matriz alterada com , como separador


um pouco de diversão sem usar matriz temporária

perl -ne 'print join ",", map {/^"/ ? s/"(.*)-"$/"-$1"/r : s/(.*)-$/-$1/r} /"[^"]+"|[^,]+/g'
    
por 06.12.2017 / 07:01
0

Com sed implementações suportando -E , supondo que as aspas duplas incorporadas nos campos de string sejam codificadas como "" e que esses campos de string não contenham caracteres de nova linha:

sed -E '
  :1
  s/^(("[^"]*"|[^"])*,)?([0-9.]+)-(,|$)/-/;
  s/^(("[^"]*"|[^"])*,)?"([0-9,.]+)-"(,|$)/"-"/
  t1' < file

Dependendo da natureza da entrada, você pode querer ser mais rigoroso ao combinar os números. Por exemplo, ([0-9.]+)- aqui corresponderia a 12- , mas também a ...- . Se esse tipo de entrada puder ocorrer na entrada, você poderá alterá-la para ([0-9]*\.?[0-9]+)- , por exemplo.

    
por 06.12.2017 / 11:49