どっかで、これに関する記事を見た気がするんだけど、思い出せない。今の自分にホットな話題なので、記しておく。車輪の再発明上等。Perl Monger or Perl Mohican の方々のツッコミ歓迎。
MacのExcelでcsvを吐くと改行コードがLF
- さっと表っぽいものを作るとき、Excel はてっとりばやい
- Mac の Excel からCSVを吐くと文字コードがShift_JIS
- さらに改行コードが CR (古いMacのデフォルト)
- これをPerlから読む( open する)と適切に読めない
- Mac OS X の改行コードは LF であり、それが見つからないので読み込みの終端に辿りつけない
プログラマーにはMac使ってるひとが多いと思うが、Macの人がExcel使ってCSVに吐き出しをすると、大抵これではまる。
かつての自分はこれでよくはまってた。
OSによって改行コードは違う
- Linux(そして今のMac)は LF (Line Feed)
- 古いMac(Mac OS 9 以前)は CR (Carriage Return)
- Windowsは CR + LF
Perlで読む前にCSVを別の改行コードで保存する?
それでもいいと思うけど、たくさんCSVがある場合、いちいち手作業で変換し保存するのはめんどくさい。
ちなみに、文字コード・改行コードを明示的に指定して保存したい場合は、ミミカキエディットが便利です。
メニューバーをカスタマイズすると、こんなふうに直接的に文字コード・改行コードを変更するUIになります。
僕はSublime Textを使いはじめるまで、テキストエディタはずっとこの mi でした。
Sublime Textだと文字コードを指定して保存は、UTF-8が前提なので、日本語文字コードに関してはできないにひとしいし、改行コードは指定すら出来ないと思う。
sed とか awk とか使えばいいんじゃない?
それは一理あるけど、僕は sed とか awk とかわからない。
がんばってPerlでopenする?
Perlのopen関数は、OSのデフォルトの区切り文字をファイルの読み込みの際に使う。ダイヤモンド演算子( <$fh> の <> )はファイルハンドルから一行読むものだけど、1行というのは、その区切り文字までのことを指す。
なので、この区切り文字を、一時的に書き換えてやればいい。
local $/ = "\r";
open my $fh, '<:crlf:encoding(Shift_JIS)', $file;
my @lines = <$fh>;
close $fh;
局所的に書き換えるために、 local を使う。
区切り文字は $/ という特殊変数がそれに該当する。
かつてのMacの改行文字(CR)をあらわすのが「 \r (または ¥r )」で、localで$/を一時的に \r に書き換える。
そしてopenする。今回はShint_JISなので、openの際にencodingを指定してやる。さらに、テキストモードで開くために :crlf も書く。
この :crlf は実は自分でもよくわかってない。普段、こんな指定をしなくてもファイルはopenできるのに、なんで書かなきゃいけないの?
普段、プログラムを書くときに
open my $fh, '<:encoding(EUC-JP)', $file;
というのはよく書くけど、:crlf や :bytes は書くことがないし、書かなくても目的は十分に達することができるので、そのあたりはよくわからない。
「バイナリで開く」か「テキストで開くか」を明示的に切り替えるときに使うようで、バイナリで開く場合は、 :bytes と書く。
ずっと、 :crlf があるなら、 :cr や:lf もあるのかと思っていたけど、それはない。:crlf か :bytes の二択。
追記:あれー、なんか :crlf を消しても普通に動いた。あれー?
まとめ
- $/ = "\r"
:crlf
改行文字が CR のファイルをPerl でopen する場合、上記の2点1点が必ず必要になるみたい。(どちらか片方だけじゃだめ)
これがBest Practice ?
かどうかは僕にはまだわからない。少なくとも、この方法ならできるとしか。