PerlによるCSVの読み込みとCSVをTSVに変換するワンライナー
2012-03-09-1
[Programming]
PerlによるCSVの読み込み方法。
既存のモジュール(Text::CSV等)を使わない場合について。
■コード(cvs.pl):
「\x07\x08」は一時退避用の適当な無意味文字列で、中身とマッチしなければOK。
■実行例:
テストファイル(csv-test.txt):
実行結果:
■ワンライナー:
応用として、CSVをTSVに変換するワンライナー。
CSVファイルの中にタブ文字がない前提。
あと改行入りカラムがない前提。
- CSVファイルフォーマットの解説:CodeZine
http://codezine.jp/article/detail/2364
- Common Format and MIME Type for Comma-Separated Values (CSV) Files
http://www.rfc-editor.org/rfc/rfc4180.txt
-- 今更だけどCSVのドキュメント[2005-12-06-2]
- perl - CSVはText::CSV(_XS)?で (404 Blog Not Found)
http://blog.livedoor.jp/dankogai/archives/50765677.html
追記150128: 改行入りのカラムに対応しました。全面的に書き換えています。書き換え前のバージョンはウェブ魚拓で見てください。
既存のモジュール(Text::CSV等)を使わない場合について。
- 今回の方法で想定するCSVフォーマット
- 区切り文字はカンマ。
- 各カラムはダブルクォートで囲み、内部のダブルクォートは二重にする。
- カラム内にカンマ、ダブルクォート、改行コードがない場合はダブルクォートで囲まなくてもよい。
- 注意
- 警告なしに非対応フォーマットでも読み込んでしまう。「ダブルクォートの閉じ忘れ」など。
- 「...,"",...」のように空要素をダブルクォートで囲んだ場合は「""」が出てきてしまう。
■コード(cvs.pl):
#!/usr/bin/env perl use strict; use warnings; my $line = ""; while (<>) { $line .= $_; my @dqm = $line =~ /\"/g; # double quote を数える next if @dqm % 2; # 奇数なら次の行を末尾に足す chomp $line; { # 何かの処理 print "\nINPUT: [$line]\n"; my @c = split_csv($line); print "RESULT: ".join(", ", map {"($_)[$c[$_]]"} 0..$#c)."\n"; } $line = ""; } sub split_csv { my ($s) = @_; $s =~ s/""/\x07\x08/g; my @rv = ("$s," =~ /("[^"]+"|[^,]+|),/gs); return map {s/^"(.*)"$/$1/gs; s/\x07\x08/"/g; $_} @rv; }
「\x07\x08」は一時退避用の適当な無意味文字列で、中身とマッチしなければOK。
■実行例:
テストファイル(csv-test.txt):
"ABC",DEF,"GHI,JKL" "AB""C",,"D"",EF","," this,foo that,"foo+ bar",100 "4/ 25","31/ 365" hoge,"hoge","hoge" "hoge",hoge "hoge","hoge/ hoge",hoge
実行結果:
% ./csv.pl csv-test.txt INPUT: ["ABC",DEF,"GHI,JKL"] RESULT: (0)[ABC], (1)[DEF], (2)[GHI,JKL] INPUT: ["AB""C",,"D"",EF",","] RESULT: (0)[AB"C], (1)[], (2)[D",EF], (3)[,] INPUT: [this,foo] RESULT: (0)[this], (1)[foo] INPUT: [that,"foo+ bar",100] RESULT: (0)[that], (1)[foo+ bar], (2)[100] INPUT: ["4/ 25","31/ 365"] RESULT: (0)[4/ 25], (1)[31/ 365] INPUT: [hoge,"hoge","hoge"] RESULT: (0)[hoge], (1)[hoge], (2)[hoge] INPUT: ["hoge",hoge] RESULT: (0)[hoge], (1)[hoge] INPUT: ["hoge","hoge/ hoge",hoge] RESULT: (0)[hoge], (1)[hoge/ hoge], (2)[hoge]
■ワンライナー:
応用として、CSVをTSVに変換するワンライナー。
CSVファイルの中にタブ文字がない前提。
あと改行入りカラムがない前提。
perl -nle '$,="\t";$_.=",";s/""/\t/g;print map{s/^"(.*)"$/$1/g;s/\t/"/g;$_}/("[^"]+"|[^,]+|),/g;' csv-test.txt
参考
- CSVファイルフォーマットの解説:CodeZine
http://codezine.jp/article/detail/2364
- Common Format and MIME Type for Comma-Separated Values (CSV) Files
http://www.rfc-editor.org/rfc/rfc4180.txt
-- 今更だけどCSVのドキュメント[2005-12-06-2]
- perl - CSVはText::CSV(_XS)?で (404 Blog Not Found)
http://blog.livedoor.jp/dankogai/archives/50765677.html
追記
追記150128: 改行入りのカラムに対応しました。全面的に書き換えています。書き換え前のバージョンはウェブ魚拓で見てください。