Manipulando vírgula em valores de string em um arquivo CSV [duplicado]

3

Eu tenho um arquivo separado por vírgulas que possui colunas numéricas e de string. Colunas de strings são citadas e podem ter vírgulas entre as aspas. Como faço para identificar as colunas com FS ="," ?

Registros de amostra

"prabhat,kumar",19,2000,"bangalore,India"

Em AWK , deve ser

$1 = "prabhat,kumar"
$2 = 19
$3 = "2000"
$4 = "bangalore,india"

A definição de FS="," está a criar o problema.

A entrada é:

"prabhat,kumar",19,2000,"bangalore,India","ABC,DEF","GHI",123,"KLM","NOP,QRS"
"prabhat,kumar",19,2000,"bangalore,India","ABC,DEF","GHI",123,"KLM","NOP,QRS"

O resultado deve ser:

"prabhat,kumar"|19|2000|"bangalore,India"|"ABC,DEF"|"GHI"|123|"KLM"|"NOP,QRS"
"prabhat,kumar"|19|2000|"bangalore,India"|"ABC,DEF"|"GHI"|123|"KLM"|"NOP,QRS"

Código que estou tentando:

awk -F"," '{for(i=1;i<=NF;i++){if(i%NF==0){ORS="\n"} {if($i ~ /^\"/ || $i ~ /\"$/) {a=a OFS $i;j++;{if(j%2==0){sub(/^\,/,X,a); print a;j=0;a=""}}} else {print $i}}} {ORS="|"}}' ORS="|" OFS=, p.txt
    
por prabhat diwaker 11.08.2014 / 13:53

6 respostas

5

Primeiro de tudo, você deve usar um analisador de CSV adequado. Por exemplo, em Perl, você pode usar Text::CSV :

  1. Instale cpanm (se você usar o Perl, me agradecerá mais tarde)

    $ sudo apt-get install cpanminus
    

    Se você não está em um sistema Debian, você deve ser capaz de instalá-lo usando o gerenciador de pacotes da sua distribuição.

  2. Instale o módulo Text::CSV

    $ sudo cpanm Text::CSV
    
  3. Analise seu arquivo

    $ perl -MText::CSV -le '
        $csv = Text::CSV->new({binary=>1}); 
        while ($row = $csv->getline(STDIN)){
        print "1:$row->[0], 2:$row->[1], 3:$row->[2], 4:$row->[3]"}' < file.csv 
    1:prabhat,kumar, 2:19, 3:2000, 4:bangalore,India
    

    Como você pode ver acima, o primeiro campo é $row->[0] , o segundo $row->[1] etc.

Esse foi o caminho correto. Um hack simples, mas sujo, seria substituir quaisquer vírgulas citadas por outro caractere. Em seguida, use awk normalmente e, finalmente, mude-os novamente para vírgulas. Estou usando ### aqui, mas você pode usar qualquer coisa que tenha certeza de que nunca estará em um dos seus campos.

$ sed -r 's/("[^",]+),([^",]+")/###/g' file.csv | 
    awk -F, '{print $1,$3}' | sed 's/###/,/g'
"prabhat,kumar" 2000
    
por 11.08.2014 / 15:09
3

Se você tem o GNU awk :

$ awk -vFPAT='[^,]*|"[^"]*"' '{ gsub("^\"|\"$","",$1); gsub("^\"|\"$","",$4); print $1 $4} '
prabhat,kumarbangalore,India

O formato de saída é um pouco feio, já que imprimi apenas $1 e $4 ao lado um do outro - tenho certeza de que você pode alterá-lo ao seu gosto.

Se você precisar manter as aspas duplas nos campos, remova as duas funções gsub(); .

Explicação:

Normalmente, awk separa campos em um registro pelo conteúdo da variável FS (Field Separator), cujo padrão é qualquer espaço em branco (tabulação, espaço e nova linha). O separador informa awk quando um registro termina. Em um arquivo csv , um registro termina em uma vírgula (passado para awk as -vFS=, ), mas é claro, em exemplos semelhantes aos seus, isso é muito simples e quebra.

Como alternativa, o FPAT (padrão de campo) define o registro em awk . Em vez de informar awk onde o registro termina, você cria uma definição que engloba um registro inteiro. No complexo csv do seu exemplo, isso é [^,]*|"[^"]*"

Isso se divide da seguinte forma: -

  • Caracteres que não são vírgulas ( [^,] ) tantas vezes quanto possível ( * ). Tudo entre duas vírgulas é um campo.
  • Ou ( | )
  • Uma aspa dupla ( " ) seguida por aspas duplas ( [^"] ) tantas vezes quanto possível ( * ) seguida por uma aspa dupla ( " ). Tudo dentro de aspas duplas (incluindo uma vírgula) conta como um campo.
por 11.08.2014 / 15:11
1

Ruby é útil para análise de CSV:

ruby -rcsv -ne 'puts CSV.generate_line(CSV.parse_line($_), :col_sep=>"|")' file
prabhat,kumar|19|2000|bangalore,India|ABC,DEF|GHI|123|KLM|NOP,QRS
prabhat,kumar|19|2000|bangalore,India|ABC,DEF|GHI|123|KLM|NOP,QRS

Observe que não há cotações nas saídas. Isso ocorre porque nenhum dos campos contém o separador de campo. Se você precisar de cotações, poderá citar forçosamente todos os campos (até inteiros):

ruby -rcsv -ne 'puts CSV.generate_line(CSV.parse_line($_), :col_sep=>"|",:force_quotes=>true)' file
"prabhat,kumar"|"19"|"2000"|"bangalore,India"|"ABC,DEF"|"GHI"|"123"|"KLM"|"NOP,QRS"
"prabhat,kumar"|"19"|"2000"|"bangalore,India"|"ABC,DEF"|"GHI"|"123"|"KLM"|"NOP,QRS"
    
por 11.08.2014 / 20:27
0

Você pode usar perl :

$ echo '"prabhat,kumar",19,2000,"bangalore,India"' |
  perl -F',(?![[:alpha:]])' -anle 'print "$F[0] $F[3]"'
"prabhat,kumar" "bangalore,India"
    
por 11.08.2014 / 14:07
0

Isso funcionou para mim:

$ echo '"prabhat,kumar",19,2000,"bangalore,India"' | 
  awk -F, '{print $1,$2,$3,$4,$5,$6}'| 
    awk -F\" '{print $2,$3,$4}'|awk -F\  '{print $1","$2,$3,$4,$5","$6}''
    
por 11.08.2014 / 15:26
0

Provavelmente um qsv seria mais sua velocidade?

sed 's/.*/,&,/;:t
s/,"/"/;s/",/"/;s/,\([^"]*\),/"/;tt
' <<\DATA
"prabhat,kumar",19,2000,"bangalore,India"
DATA

OUTPUT

"prabhat,kumar"19"2000"bangalore,India"
    
por 11.08.2014 / 23:46