Perl でShift-JIS の固定長テキストをバラしてutf8 変換してからデータベースに取り込む処理を書いたのですが、すんごく初歩的なところでミスしたので自戒の意味も込めてメモ。
修正前のコード
どこがマズかったかわかりますでしょうか?
use Encode; # Shift-JIS の固定長テキスト my_shift_jis.csv を開く my $path = "my_shift_jis.csv"; open my $file, '<', $path or die; # ファイルの内容を1行ずつ取り出す my @lines = (); while ( my $line = readline $file ) { # utf8 に変換してから $line = encode('utf8', decode('MS932', $line)); # 配列に放り込む push @lines, $line; } # ファイルはとっとと閉じる close $file; # 配列に格納した内容を1行ずつ取り出す foreach my $line( @lines ) { # 固定長構成にしたがって分割し、配列内に値を格納 my @values = unpack 'a3a1a4a1a4a10a10', $line; # 配列から値を1つずつ取り出す foreach my $value( @values ) { # ここで取り出した値を処理 } }
修正後のコード
use Encode; # Shift-JIS の固定長テキスト my_shift_jis.csv を開く my $path = "my_shift_jis.csv"; open my $file, '<', $path or die; # ファイルの内容を1行ずつ取り出す my @lines = (); while ( my $line = readline $file ) { # 配列に放り込む push @lines, $line; } # ファイルはとっとと閉じる close $file; # 配列に格納した内容を1行ずつ取り出す foreach my $line( @lines ) { # 固定長構成にしたがって分割し、配列内に値を格納 my @values = unpack 'a3a1a4a1a4a10a10', $line; # 配列から値を1つずつ取り出す foreach my $value( @values ) { # utf8 に変換してから $value = encode('utf8', decode('MS932', $value)); # ここで取り出した値を処理 } }
文字コード変換は値を使う直前に行う
どこがマズかったか、修正前と修正後のコードを比較すれば一目瞭然ですね。
修正前はファイルから行データを引っこ抜いた段階でそそくさとShift-JIS(MS932)からutf8に変換しておりました。
これに対して修正後は、行データを引っこ抜いて、固定長分割して、分割後の値をさあ使うぞ!という直前でutf8変換しております。
なんで使う直前にutf8変換するのかと言うと、文字コード変換によって行あたりの文字数が変化する場合があるからです。
例えば半角カナが混じっている様なデータは、この典型でしょう。
デコード→エンコードの処理によって固定長だったテキストデータが可変長状態になってしまうわけです。
こうなった後で固定長構成に従って分割しようとしても上手く行くわけがありません。
ですから、固定長テキストを文字コード変換してから処理する場合は、分割が済んでから変換する様にしましょう。
という、ちょっと恥ずかしい失敗談でした。