次のような形式のcsvファイルがあります。
"col1","col2","col3","col4"
"1","text1","<p>big
html
text</p>
","4th column"
"2","text2","<p>big2
html2
text2</p>
","4th column2"
4列目を使って抽出したい。私はawk
がこれに最適なツールだと思います(私が間違っていたら知らせてください)。私はこれを試しました
awk -F, '{print $4}' myFile.csv
しかし失敗します。 3列目は複数行なので。 awk
または他のUNIXコマンドを使用して4番目の列を抽出するにはどうすればよいですか。実際のファイルが大きい(> 2GB)ので、効率的なソリューションを探しています
更新:
実際、はるかに簡単な方法は、gawk
にレコード区切りを設定することです。
$ gawk 'BEGIN{RS="\"\n"; FS=","}{print $4}' myFile.csv
"col4
"4th column
"4th column2
ただし、これにより、各列の末尾から末尾の"
が削除されます。これを修正するには、自分で印刷できます。
$ gawk 'BEGIN{RS="\"\n"; FS=","}{print $4"\""}' myFile.csv
"col4"
"4th column"
"4th column2"
引用符がまったく不要な場合は、フィールド区切り記号を","
に設定できます。
$ gawk 'BEGIN{RS="\"\n"; FS="\",\""}{print $3}' myFile.csv
col3
4th column
4th column2
私が考えることができる唯一の方法 これを行う1つの方法は、最初にファイルを変更してから解析することです。あなたの例では、2つのレコードを実際に区切る改行は、常に"
に従っています:
"col1","col2","col3","col4" <-- here
1,"text1","<p>big <-- no "
これがファイル全体に当てはまる場合は、"
の直後ではないすべての改行をプレースホルダーに置き換えることができるため、すべてを1行で記述できます。その後、gawk
を使用して通常どおり解析し、最後にプレースホルダーを改行で再度置換できます。文字列&%&
は、ファイルに存在する可能性が低いため、プレースホルダーとして使用します。
$ Perl -pe 's/"\s*\n/"&%&/; s/\n//g; s/&%&/\n/;' myFile.csv | awk -F, '{print $4}'
"col4"
"4th column"
"4th column2"
Perl
の-p
フラグは、print each line of the input file
で指定されたスクリプトを適用した後の-e
を意味します。次に、3つの置換(s/foo/bar/
)コマンドがあります。
s/"\s*\n/"&%&/
:"
の後に0個以上の空白文字(\s*
)と改行文字(\n
)が続くものを検索します。それを"&%&
に置き換えます。引用符は形式を維持するために追加されており、&%&
はランダムなプレースホルダーです。ファイルに表示されないものでもかまいません。
s/\n//g;
:実際の改行がプレースホルダーに置き換えられたため、このレコードの残りの改行をすべて安全に削除できるようになりました。つまり、現在のレコードのすべての行が現在の行に連結されています。
s/&%&/\n/
:プレースホルダーを通常の新しい行に戻します。
コマンドの出力を理解するには、gawk
なしで実行します。
$ Perl -pe 's/"\s*\n/"&%&/; s/\n//g; s/&%&/\n/;' myFile.csv
"col1","col2","col3","col4"
1,"text1","<p>big html text</p>","4th column"
2,"text2","<p>big2 html2 text2</p>","4th column2"
これで、長いレコードが1行になり、これはgawk
に最適な食べ物です。
Perlで直接行うこともできます。
Perl -ne '$/="\"\n"; chomp;@a=split(/,/);print "$a[3]\"\n"' myFile.csv
"col4"
"4th column"
"4th column2"
これはもう少しPerlマジックを使用しています。 $/
special変数は、入力レコードの区切り記号です。 "\n
に設定することで、\n
ではなく"\n"
でのみ行を分割するようにPerlに指示し、各レコードが単一行として扱われるようにします。それが完了すると、chomp
は改行を行末から削除し(後で印刷するため)、split
は各レコードを分割し(,
で)、配列に保存します@a
。最後に、4番目の列である配列の4番目の要素(配列には0から番号が付けられるため、$a[3]
)を出力します。
さらに魔法をかけるには、自動吐き出し(-a
)をオンにして、コンマ(F","
)で分割します。これにより、各レコードが特別な@F
配列に分割され、配列の4番目の要素を出力できます。
$ Perl -F"," -ane '$/="\"\n";chomp;print "$F[3]"' myFile.csv
"col4"
"4th column"
"4th column2"
戦闘テスト済みのCSV解析モジュールを使用することをお勧めします。例えば:
Perl -MText::CSV -E '
$csv = Text::CSV->new({binary=>1});
while ($row = $csv->getline(STDIN)) {say $row->[3]}
' < file.csv
col4
4th column
4th column2
またはこれは同じ結果を生成します:
Ruby -rcsv -e 'CSV.foreach(ARGV.shift) {|row| puts row[3]}' file.csv
Python:
python -c "import csv,sys; print '\n'.join([ r[3] for r in csv.reader(open(sys.argv[1]))])" myfile.csv
リストを介してファイルのコンテンツをメモリにロードする上記のアプローチとは異なり、一度に1行ずつファイルを反復処理する大きなファイルのメモリ保護ソリューション
#!/usr/bin/env python
import sys
import csv
with open(sys.argv[1]) as f:
for row in csv.reader(f):
print(row[3])
すべてのソリューションのテスト結果:
OS:Ubuntu 12.04
公開CSVデータのダウンロード: http://seanlahman.com/baseball-archive/statistics/
バージョンの詳細
root@ubuntu:~# python --version
Python 2.7.3
root@ubuntu:~# Ruby --version
Ruby 1.8.7 (2011-06-30 patchlevel 352) [i686-linux]
root@ubuntu:~# Perl --version
This is Perl 5, version 14, Subversion 2 (v5.14.2) built for i686-linux-gnu-thread-multi-64int
time
の結果
root@ubuntu:~# time python -c "import csv,sys; print '\n'.join([ r[3] for r in csv.reader(open(sys.argv[1]))])" Master.csv > /tmp/python
real 0m1.112s
user 0m0.056s
sys 0m0.316s
root@ubuntu:~# time Ruby -rcsv -e 'CSV.foreach(ARGV.shift) {|row| puts row[3]}' Master.csv > /tmp/Ruby
real 0m24.582s
user 0m23.397s
sys 0m0.448s
root@ubuntu:~# time Perl -MText::CSV -E '
> $csv = Text::CSV->new({binary=>1});
> while ($row = $csv->getline(STDIN)) {say $row->[3]}
> ' < Master.csv > /tmp/Perl
real 0m7.049s
user 0m5.876s
sys 0m0.468s
uNIXスタイルの場合、「\ n」で終了する行
tr -d "\n" < myfile.csv | awk 'BEGIN{RS=","} !(NR % 4)'
一部のフィールドはmulti-lines _tr -d "\n"
_はすべての改行文字を削除し、「、」で区切られた値のストリームを作成します。 awkは、行区切り文字として「、」を使用し、(行番号モジュロ4)が0になるたびに出力するように指示されます。
これは、4番目のフィールドが(サンプルのように)最後のフィールドである場合にのみ機能します。そうでない場合:
tr -d "\n" < myfile.csv | awk 'BEGIN{RS=","; last=12} (++c == 4) (c == last) {c=0}'
行をカウントし、countが4の場合に行を出力し、最後のフィールドに達したときにカウントをリセットします。
以下を試してください:
while IFS=',' read -r a1 a2 a3 a4
do
echo "$a4" >> urname.csv
done < input.csv
これを行うことができた最も簡単な方法は、単に csvtool を使用することでした。 csvtoolを使用する他のユースケースもありました。列データ自体に表示されている場合、引用符または区切り文字を適切に処理できます。
csvtool format '%(4)\n' input.csv
4を列番号に置き換えると、探している列データを効果的に抽出できます。
を使用してCSVの4番目の要素を取得します。
cut -d , -f 4 myFile.csv
次のコマンドでファイルに保存します。
cut -d , -f 4 myFile.csv | cat >> my4thEltsFile.csv