過去の自分に助けられたり物足りなかったり
毎回書いてる気がしますが、ブログに学んだことを書いておくと何が良いかというと、過去の自分に助けてもらえたりします。
ということで、今日も(自分の仕事を楽にするために)Perl 書いているのですが、ちょっとつまづいたので書いておきます。
きっと数ヶ月、数年先の自分が見ることになると思うので・・・
Perl で 弥生販売21 の csv ファイルを読み込んで表示する
まず、環境ですが macOS Big Sur 上で動く Perl です。
扱うのは Windows 上で動く会計ソフト「弥生販売」から出力されたファイルです。文字コードは Windows おなじみの cp932 、というところまでは分かっています。
過去の自分のブログ記事も見て、読み込むコードを書きます。
#!/usr/bin/env perl use strict; use warnings; use Encode qw(encode_utf8); my $file = '入金区分別入金明細表.csv'; open my $FH, '<:encoding(cp932)', $file or die; # 文字コードを指定してファイル読み込み for my $line (<$FH>) { chomp $line; print encode_utf8($line); } close $FH;
はい、さくっと表示できます。ちなみに、中身は当然ダミーのデータなので会社名や所在地に意味はありません。
1,1,0,20211228,123,10,0,0,0,0,234,,17,1,2,,201,振込 白猫銀行,0,,0,0,,0,0,1000,,0,0,0,,0,0,,,,,,,A株式会社,,,,,,,,,,,,,A株式会社,000-0000,東京都葛飾区堀切1-655-5,東ビル2F,00-0000-0000 1,1,0,20211228,124,10,0,0,0,0,456,,,1,2,,201,振込 白猫銀行,0,,0,0,,0,0,2000,,0,0,0,,0,0,,,,,,,B株式会社,,,,,,,,,,,,,B株式会社,000-0001,東京都西東京市北町2-115-3,西タワー,00-0000-0001
Perl で 弥生販売21 の csv ファイルをパースするとエラー
では、ここで各行を csv として解釈し、配列に入れるべくコードを変えていきます。
これも過去の自分の記事みてコピペです・・・が、どうもうまくいきません。
#!/usr/bin/env perl use strict; use warnings; use Encode qw(encode_utf8); use Text::CSV_XS; my $csv = Text::CSV_XS->new(); # csvを扱う便利オブジェクト my $file = '入金区分別入金明細表.csv'; open my $FH, '<:encoding(cp932)', $file or die; for my $line (<$FH>) { chomp $line; my $status = $csv->parse($line); # CSV文字列をパースしてフィールド群に切り分ける # $status は成否判定が入っている(今回は使わない) my @columns = $csv->fields(); # パースされたフィールド群を配列に入れる print "@columns" . "\n"; }
本来なら2行表示されるはずが、1行分しか表示されません。
Use of uninitialized value $columns[0] in join or string at /Users/sironekotoro/urikake2kaikei.pl line 18, <$FH> line 2. 1 1 0 20211228 124 10 0 0 0 0 456 1 2 201 振込 白猫銀行 0 0 0 0 0 2000 0 0 0 0 0 B株式会社 B株式会社 000-0001 東京都西東京市北町2-115-3 西タワー 00-0000-0001
調べてみる
テキストエディタでこの csv ファイルを見てみましが、おかしそうなところは「見えません」でした。
そう、このエラーの原因は見えないのです。
しかし、過去の自分がしっかりとヒントを書いていました。
# $status は成否判定が入っている(今回は使わない)
やるじゃん俺・・・
$csv->parse($line);
は引数の $line
が csv としてパースできれば 1 , 失敗すれば 0 を返します。
$status
を表示してみます。
#!/usr/bin/env perl use strict; use warnings; use Encode qw(encode_utf8); use Text::CSV_XS; my $csv = Text::CSV_XS->new(); # csvを扱う便利オブジェクト my $file = '入金区分別入金明細表.csv'; open my $FH, '<:encoding(cp932)', $file or die; for my $line (<$FH>) { chomp $line; my $status = $csv->parse($line); # CSV文字列をパースしてフィールド群に切り分ける print $status . "\n"; # 成否判定のみ表示する } close $FH;
実行してみると、1行目のパースに失敗しているようです。
0 1
では、その原因は・・・?
ということで、Text::CSV_XS でエラーを切り分けてくれるオプションがありました。auto_diag
です。
早速、オプションをつけてみます。
my $csv = Text::CSV_XS->new( { auto_diag => 1 } ); # csvを扱う便利オブジェクト
おー、なんかエラーが出ていますね。改行コード(CR)がクオートされていない内側にある?(英検三級並感)
# CSV_XS ERROR: 2032 - EIF - CR char inside unquoted, not part of EOL @ rec 0 pos 217 field 57 0 1
で、このエラーでググると、解決法を残してくれているブログが見つかったのでした。ありがたいです。
制御コードが悪さをしていたようですね。どうして入り込んだのかはわからないのですが・・・
制御コードを正規表現で置換して削除しつつ、さらにパースが失敗したときにはプログラムを終了させる die
も仕掛けておきます。
#!/usr/bin/env perl use strict; use warnings; use Encode qw(encode_utf8); use Text::CSV_XS; my $csv = Text::CSV_XS->new( { auto_diag => 1 } ); # csvを扱う便利オブジェクト my $file = '入金区分別入金明細表.csv'; open my $FH, '<:encoding(cp932)', $file or die; for my $line (<$FH>) { chomp $line; # https://www.drk7.jp/MT/archives/001934.html を参考に制御文字を削除 $line =~ s/[\x01-\x1f]+$//gsm; my $status = $csv->parse($line); die "Parse Error!" if $status == 0; # パースに失敗してたらプログラムを終了 my @columns = $csv->fields(); print encode_utf8("@columns") . "\n"; } close $FH;
これでちゃんとパースできているようです。エラーもありません。
1 1 0 20211228 123 10 0 0 0 0 234 17 1 2 201 振込 白猫銀行 0 0 0 0 0 1000 0 0 0 0 0 A株式会社 A株式会社 000-0000 東京都葛飾区堀切1-655-5 東ビル2F 00-0000-0000 1 1 0 20211228 124 10 0 0 0 0 456 1 2 201 振込 白猫銀行 0 0 0 0 0 2000 0 0 0 0 0 B株式会社 B株式会社 000-0001 東京都西東京市北町2-115-3 西タワー 00-0000-0001
この後は、これを弥生会計に取り込めるように編集し、cp932 で保存し・・・という作業です。
この作業はなぜ必要?
うちの経理業務では会計ソフトとして「弥生会計」、売上管理に「弥生販売」を利用しています。
同じシリーズの製品なので、当然データの取り込みも可能です。
可能なのですが、「過去の仕訳・業務との整合性」「深淵な理由」などにより標準機能での取り込みを行っていません。
現在は弥生販売から一旦 csv に出力し、加工した後に弥生会計に取り込み、という手順を踏んでいます。
こういった作業は他にもあり、単純に面倒なのでプログラムでやらせるに限るよな!ってことで Perl で書いたりしています。