Text::CSV で Error 2032 が出たときの対処方法
久々に Perl を使って csv ファイルの処理を行いました。
カンマ区切りの csv ファイルや、タブ区切りの tsv ファイルを扱うにあたって、単純に split 関数を使って文字列を区切る処理では、各フィールド内にカンマやタブが混入したデータの場合に正常に切り出せません。
散々既出だと思うので、あえて今更説明するまでもないと思いつつも、簡単な実例を交えて説明してみます。具体的には下記のようなプログラムでは、期待するフィールドに切り出すことができません。
use Data::Dumper; my $line = qq{"aaa,a","bbb,b","ccc,c"}; print Dumper split /,/, $line;
期待する結果は、"aaa,a"、"bbb,b"、"ccc,c" の3カラムに切り出したいところですが、下記のように意図しない区切りで切り出されてしまいます。単純にカンマを delimiter として分割しているので動作としては正しいのですが、欲しい結果とは異なります。
$VAR1 = '"aaa'; $VAR2 = 'a"'; $VAR3 = '"bbb'; $VAR4 = 'b"'; $VAR5 = '"ccc'; $VAR6 = 'c"';
そんなときに威力を発揮するのが Text::CSV や Text::CSV_XS なわけです。
Text::CSV を使って意図するフィールド分割を行うコードは下記の通りになります。
use Data::Dumper; use Text::CSV; my $tc = Text::CSV->new; my $line = qq{"aaa,a","bbb,b","ccc,c"}; $tc->parse($line); print Dumper $tc->fields;
結果は期待通りに、"aaa,a"、"bbb,b"、"ccc,c" の3フィールドに分割されました。
$VAR1 = 'aaa,a'; $VAR2 = 'bbb,b'; $VAR3 = 'ccc,c';
そんなわけで、csv や tsv ファイルを Excel へ流し込んだり、集計したりする際には、Text::CSV をおとなしく使っていれば良いのですが、先日書いたコードを実行したところ、Error 2032 なるエラーでファイルが解析できない問題が発生しました。Perl 界隈においては、そんなときまずソース読めが基本ですね。
読んでみました。CSV_PP.pm に下記のようなエラー定義がありました。
my $ERRORS = { # PP and XS 1000 => "INI - constructor failed", 1001 => "sep_char is equal to quote_char or escape_char", 1002 => "INI - allow_whitespace with escape_char or quote_char SP or TAB", 1003 => "INI - \r or \n in main attr not allowed", 2010 => "ECR - QUO char inside quotes followed by CR not part of EOL", 2011 => "ECR - Characters after end of quoted field", 2021 => "EIQ - NL char inside quotes, binary off", 2022 => "EIQ - CR char inside quotes, binary off", 2025 => "EIQ - Loose unescaped escape", 2026 => "EIQ - Binary character inside quoted field, binary off", 2027 => "EIQ - Quoted field not terminated", 2030 => "EIF - NL char inside unquoted verbatim, binary off", 2031 => "EIF - CR char is first char of field, not part of EOL", 2032 => "EIF - CR char inside unquoted, not part of EOL", 2034 => "EIF - Loose unescaped quote", 2037 => "EIF - Binary character in unquoted field, binary off", 2110 => "ECB - Binary character in Combine, binary off", 2200 => "EIO - print to IO failed. See errno", # PP Only Error 4002 => "EIQ - Unescaped ESC in quoted field", 4003 => "EIF - ESC CR", 4004 => "EUF - ", # Hash-Ref errors 3001 => "EHR - Unsupported syntax for column_names ()", 3002 => "EHR - getline_hr () called before column_names ()", 3003 => "EHR - bind_columns () and column_names () fields count mismatch", 3004 => "EHR - bind_columns () only accepts refs to scalars", 3006 => "EHR - bind_columns () did not pass enough refs for parsed fields", 3007 => "EHR - bind_columns needs refs to writable scalars", 3008 => "EHR - unexpected error in bound fields", 0 => "", };
Error 2032 は終端前に制御コードが入っているのがエラーの原因と読み取れました。従って対処したコードとして、下記のように parse() を呼び出す前に、\x01-\x1f までの範囲の制御コードを削除するコードとしました。イメージとしてこんな感じです。
open my $fh, '<', $file or die; my $tc = Text::CSV->new; while(my $line=<$fh>) { $line =~ s/[\x01-\x1f]+$//gsm; $tc->parse($line); my @fields = $tc->fields; } close $fh;
以上、備忘録をかねてメモとして残しておきました。やっぱりエラーが出たときはソース読むのが手っ取り早いですね。
コメントやシェアをお願いします!