Ignore delimitador presente dentro de citações

2

Eu tenho um arquivo .csv da seguinte forma:

"ID0054XX","PT. SUMUT","18 JL.BONJOL","SUMATERA UTARA, NORTH","MEDAN","","ID9856","PDSUIDSAXXX","","","","Y"
"ID00037687","PAN INDONESIA, PT.","JALAN JENDERAL, SUDIRMAN, SENAYAN","","INDIA","","ID566543","PINBIDJAXXX","","0601","","Y"

Eu tenho um script que atribui cada um dos valores separados por vírgula a uma variável única usando , como delimitador.

A parte do script é como abaixo:

IFS=,

[ ! -f $INPUT ] && { echo "$INPUT file not found"; exit 99; }

while read Key  Name    Address1        Address2        City    State   Country SwiftCode       Nid     Chips   Aba     IsSwitching
do
          echo "-------------------------------------------------------------------"

     echo "From Key : $Key"

    echo "-------------------------------------------------------------------"
          echo "-------------------------------------------------------------------"

     echo "From Name : $Name"

O que ele faz é separar os valores com uma vírgula dentro das aspas em relação à saída desejada, separando cada valor exclusivamente para suas respectivas variáveis.

Eu tentei substituir a vírgula como IFS=[","] , mas sem sorte. Qualquer sugestão / ajuda é muito apreciada.

    
por Ashish K 06.05.2017 / 11:38

1 resposta

3

Você está fazendo algumas coisas erradas aqui:

  1. Você está usando o shell para analisar o texto.

    Embora isso seja possível, é muito ineficiente. É lento, difícil de escrever, difícil de ler e muito difícil de fazer corretamente. O shell não foi projetado para esse tipo de coisa.

  2. Você está tentando analisar um arquivo csv sem um analisador csv.

    O CSV não é um formato simples. Você pode ter campos que contenham o delimitador como você faz aqui. Você também pode ter campos abrangendo várias linhas. A tentativa de analisar dados CSV arbitrários com correspondência simples de padrões é muito, muito complicada e extremamente difícil de acertar.

A solução ruim e hacky é fazer algo assim:

$ sed 's/","/"|"/g' file.csv | 
    while IFS='|' read -r Key Name Address1 Address2 City \
     State Country SwiftCode Nid Chips Aba IsSwitching; do 
        echo "From Key : $Key"; echo "From Name : $Name"; 
    done
From Key : "ID0054XX"
From Name : "PT. SUMUT"
From Key : "ID00037687"
From Name : "PAN INDONESIA, PT."

Isso substituirá todos os "," por "|" e, em seguida, usará | como delimitador. Obviamente, isso falhará se algum dos seus campos puder conter | .

A abordagem boa e limpa é usar uma linguagem de script apropriada, não o shell, e um analisador csv. Por exemplo, em Perl 1 :

$ cat file.csv | perl -MText::CSV -le '
    $csv = Text::CSV->new({binary=>1}); 
    while ($row = $csv->getline(STDIN)){ my ($Key, $Name, $Address1, $Address2, $City, $State, $Country, $SwiftCode, $Nid, $Chips, $Aba, $IsSwitching) = @$row;
print "From Key: $Key\nFrom Name: $Name";}' 
From Key: ID0054XX
From Name: PT. SUMUT
From Key: ID00037687
From Name: PAN INDONESIA, PT.

Ou como um script:

#!/usr/bin/perl -l
use strict;
use warnings;
use Text::CSV;

open(my $fh, "file.csv");
my $csv = Text::CSV->new({binary=>1}); 
while (my $row = $csv->getline($fh)){
    my (
            $Key, $Name, $Address1, $Address2, $City,
            $State, $Country, $SwiftCode, $Nid, $Chips,
            $Aba, $IsSwitching
         ) = @$row;
    print "From Key: $Key\nFrom Name: $Name";
}

Observe que você terá que instalar o módulo Text::CSV primeiro ( cpanm Text::CSV ) e talvez queira instalar cpanm (pacote cpanminus na maioria das distribuições)

Como alternativa, no Python 3:

#!/usr/bin/env python3

import csv
with open('file.csv', newline='') as csvfile:
    linereader = csv.reader(csvfile, delimiter=',', quotechar='"')
    for row in linereader:
        print("From Key: %s\nFrom Name: %s" % (row[0], row[1]))

Salvar o código Python acima como um script e executá-lo no seu arquivo será impresso:

$ foo.py
From Key: ID0054XX
From Name: PT. SUMUT
From Key: ID00037687
From Name: PAN INDONESIA, PT.

1 Sim, estou ciente de que isso é um UUoC, mas é mais simples escrever como um liner desta forma.

    
por 06.05.2017 / 12:01