web-dev-qa-db-ja.com

引用符の中にある区切り文字を無視する

次のような.csvファイルがあります。

"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"

区切り文字として,を使用して、コンマ区切りの各値を一意の変数に割り当てるスクリプトがあります。

スクリプトの一部は次のとおりです。

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"

それが行うことは、各値をそれぞれの変数に一意に分離するという私の望ましい出力に対して、引用符の中にカンマが含まれる値を分離することです。

IFS=[","]のようなコンマを置き換えてみましたが、うまくいきませんでした。任意の提案/助けは本当に感謝しています。

4
Ashish K

あなたはここでいくつか間違っていることをしています:

  1. シェルを使用してテキストを解析しています。

    これは可能ですが、非常に非効率的です。遅く、書きにくく、読みにくく、適切に行うのが非常に困難です。シェルは、この種のもののために設計されていないだけです。

  2. Csvパーサーなしでcsvファイルを解析しようとしています。

    CSVは単純な形式ではありません。ここで行うように、区切り文字を含むフィールドを持つことができます。複数行にわたるフィールドを持つこともできます。単純なパターンマッチングを使用して任意のCSVデータを解析しようとすることは、非常に複雑であり、正しく実行するのが非常に困難です。

悪い、ハックな解決策は、次のようなことです:

$ 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."

これにより、すべての",""|"に置き換えられ、区切り文字として|が使用されます。もちろん、フィールドに|を含めることができる場合は失敗します。

優れたクリーンなアプローチは、シェルではなく適切なスクリプト言語とcsvパーサーを使用することです。たとえば、Perl1

$ 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.

または、スクリプトとして:

#!/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";
}

最初にText::CSVモジュール(cpanm Text::CSV)をインストールする必要があり、cpanm(ほとんどのディストリビューションではパッケージcpanminus)をインストールしたい場合があることに注意してください。

または、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]))

上記のPythonコードをスクリプトとして保存し、ファイルで実行すると、次のように出力されます。

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

1はい、私はそれがUUoCであることを認識していますが、この方法でワンライナーとして書く方が簡単です。

4
terdon