PerlでDBのデータを取り出し、その内容を加工してダウンロードするという要件にちょっと苦労したので、記録しておきます。
DBはMTのアンケートフォームの回答が格納されています。エンコードはUTF8。これはmysqlコマンドでcsv形式にでも出力すればいいかと思っていたら、以下の問題にぶち当たりました。
- into outfileを使おうとしたら、アカウントがファイル出力権限を持っていない。
⇒標準出力をperl側で配列に入力することに変更。 - 標準出力をperlから拾ってみたら文字化けしてる。
⇒statusコマンドで確認すると、mysqlクライアントのデフォルト接続文字コードがutf8になっていた。「–default-character-set=utf8」を付けて解決。
次の問題は、スクリプトと元データがUTF8であるのに、substrで指定した「文字数」ではなく「バイト数」で文字が取り出されてしまいます。
Perlの文字列型データには、utf8フラグなるものがあって、これがついているのは内部コード形式であるという扱いになります。フラグのあるデータと無いデータでは文字数カウントや正規表現でのマッチなどに影響が出ます。しかし、フラグ付きの文字を出力すると「Wide character in print」という警告が出ます。
基本的には出力前までは全てのデータ内部コード形式で扱い、出力(printなど)する前に「Encode::encode_utf8」を使ってフラグをはずす必要があるようです。
「use utf8;」という宣言で、スクリプト内の全てのリテラルはフラグ付きで扱われます。
さて、ここまでできたのはいいのですが、ダウンロード時にShift-JISに変換する必要があります。これはデータをエクセルで読むためです。
標準出力のエンコードを変更する「binmode STDOUT,’encoding(cp932)’;」なる指定ができるのですが、今度は「utf8フラグ」が付いている状態でprintに渡さないと、正常なマッピングができないエラーになります。
せっかくprint前に「encode_utf8」でフラグを外しましたが、逆に全ての出力前に「decode_utf8」でフラグ付きに変換。やっとShift-JISでファイルのダウンロードができました。