1

このtest.csvファイルに似たCSVファイルがあります。

Header 1; Header 2; Header 3
A;B;US
C;D;US
E;F;US
G;H;FR
I;J;FR
K;L;FR
M;"String with ; semicolon";UK
N;"String without semicolon";UK
O;"String OK";
P;"String OK";

ここで、ヘッダー3に基づいてこのファイルを分割します。したがって、「US」、「FR」、「UK」、および「」用の4つの個別のCSVファイルになります。

私の非常に限られたLinuxコマンドラインスキルで(悲しいことに:-(私は今までこの行を使用していました:

awk -F\; 'NR>1{ fname="country_yearly_"$3".csv"; print >>(fname); close(fname);}' test.csv

もちろん、経験豊富なコマンドラインユーザーは私の問題に気付くでしょう。test.csvの1つのフィールドに、引用符でマークされたフィールドにも区切り文字として使用するセミコロンが存在する行が含まれています(できません)。何百万もの行があるので確かにそれを保証します、しかし私はこれを仮定する答えに満足しています)。悲しいことに、country_yearly_ semicolon ".csvという名前の追加ファイルがあります。このファイルには、私の例ではこの行が含まれています。

この問題を解決するための私の冒険で、私はSOでこの質問に出くわしました。特に、トールの答えには、文字列内のすべてのセミコロンを置き換えることによる私の問題の解決策が含まれているようです。私はそれに応じて彼のコードを次のように調整しました:

awk -F'"' -v OFS='' '
  NF > 1 { 
    for(i=2; i<=NF; i+=2) { 
      gsub(";", "|", $i);
      $i = FS $i FS;       # reinsert the quotes
    }
    print
  }' test.csv > test1.csv

これで、次のtest1.csvファイルを取得します。

M;"String with | semicolon";UK
N;"String without semicolon";UK
O;"String OK";
P;"String OK";

ご覧のとおり、引用符が付いているすべての行が表示され、問題の行も修正されていますが、a)引用符で囲まれている行だけでなく、実際にはすべての行が必要であり、彼のコードのどの部分がそうであるかわかりません。行を引用符で囲んだものに制限します。b)出力を新しいファイルに送信するのではなく、test.csvを変更するだけの方が効率的だと思いますが、その方法もわかりません。

Bireiの答えに応じて編集:

残念ながら、私の最小限の例は単純すぎました。更新されたバージョンは次のとおりです。

Header 1; Header 2; Header 3; Header 4
A;B;US; 
C;D;US;
E;F;US;
G;H;FR;
I;J;FR;
K;L;FR;
M;"String with ; semicolon";UK;"Yet another ; string"
N;"String without semicolon";UK; "No problem here"
O;"String OK";;"Fine"
P;"String OK";;"Not ; fine"

私の実際のデータには約100列と数百万行があり、文字列のセミコロンを無視した国の列は列13であることに注意してください。ただし、私が見る限り、列13であるという事実を使用できません。 t最初に文字列のセミコロンを削除します。

4

2 に答える 2

4

ファイルを分割するには、次のようにします。

awk -v FS=";" '{ CSV_FILE = "country_yearly_" $NF ".csv" ; print > CSV_FILE }'

これは常にファイル名を構成するために最後のフィールドを取ります。

この例では、パターンのために引用符付きの行のみが印刷されNF > 1ます。次のスクリプトは、すべての行を出力します。

awk -F'"' -v OFS='' '
  NF > 1 { 
    for(i=2; i<=NF; i+=2) { 
      gsub(";", "|", $i);
      $i = FS $i FS;       # reinsert the quotes
    }
  }
  {
    # print all lines
    print
  }' test.csv > test1.csv

やりたいことをするために、スクリプトの行を変更して再処理することができます。

awk -F'"' -v OFS='' '
  # Save the original line
  { ORIGINAL_LINE = LINE = $0 }
  # Replace the semicolon inside quotes by a dummy character
  # and put the resulting line in the LINE variable
  NF > 1 {
    LINE = ""
    for(i=2; i<=NF; i+=2) { 
      gsub(";", "|", $i)
      LINE = LINE $(i-1) FS $i FS     # reinsert the quotes
    }
    # Add the end of the line after the last quote
    if ( $(i+1) ) { LINE = LINE $(i+1) }
  }
  {
    # Put the semicolon-separated fields in a table
    # (the semicolon inside quotes have been removed from LINE)
    split( LINE, TABLE, /;/ )
    # Build the file name -- TABLE[ 3 ] is the 3rd field
    CSV_FILE = "country_yearly_" TABLE[ 3 ] ".csv"
    # Save the line
    print ORIGINAL_LINE > CSV_FILE
  }'
于 2012-09-04T11:09:19.067 に答える
1

あなたは解決策の近くにいました。二重引用符が付いたフィールドの問題を回避するために、最後のフィールドを使用します。また、各ファイルを閉じる必要はありません。awkこれらは、スクリプトの最後にシェルによって自動的に閉じられます。

awk '
    BEGIN {
        FS = OFS = ";";
    }
    FNR > 1 {
        fname = "country_yearly_" $NF ".csv";
        print >>fname;
    }
' infile

出力を確認してください:

head country_yearly_*

その結果、次のようになります。

==> country_yearly_.csv <==
O;"String OK";
P;"String OK";

==> country_yearly_FR.csv <==
G;H;FR
I;J;FR
K;L;FR

==> country_yearly_UK.csv <==
M;"String with ; semicolon";UK
N;"String without semicolon";UK

==> country_yearly_US.csv <==
A;B;US
C;D;US
E;F;US
于 2012-09-04T10:58:33.853 に答える