sironekotoroの日記

Perl で楽をしたい

Perlで弥生販売21から出力されたcsvファイルを読み込んだらパースに失敗した

過去の自分に助けられたり物足りなかったり

毎回書いてる気がしますが、ブログに学んだことを書いておくと何が良いかというと、過去の自分に助けてもらえたりします。

sironekotoro.hateblo.jp

ということで、今日も(自分の仕事を楽にするために)Perl 書いているのですが、ちょっとつまづいたので書いておきます。

きっと数ヶ月、数年先の自分が見ることになると思うので・・・

Perl で 弥生販売21 の csv ファイルを読み込んで表示する

まず、環境ですが macOS Big Sur 上で動く Perl です。

扱うのは Windows 上で動く会計ソフト「弥生販売」から出力されたファイルです。文字コードWindows おなじみの cp932 、というところまでは分かっています。

drive.google.com

過去の自分のブログ記事も見て、読み込むコードを書きます。

#!/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); は引数の $linecsv としてパースできれば 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 です。

metacpan.org

早速、オプションをつけてみます。

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

で、このエラーでググると、解決法を残してくれているブログが見つかったのでした。ありがたいです。

www.drk7.jp

制御コードが悪さをしていたようですね。どうして入り込んだのかはわからないのですが・・・

制御コードを正規表現で置換して削除しつつ、さらにパースが失敗したときにはプログラムを終了させる 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 で書いたりしています。