Windows環境のActivePerlのencodingモジュールと改行の関係

Windowsの改行コードは"\x0d\x0a"Unixでは"\x0a"などと異なりますが、Perlでは同じスクリプトを複数の環境でも動かせるように、改行コードを"\n"で扱うことができるようになっています。
実際のWindows版ActivePerlでは以下のような変換を行っています。

  • 標準入力で"\x0d\x0a""\x0a"に変換される
  • 標準出力で"\n"は(最終的に)"\x0d\x0a"に変換される
  • 標準エラーで"\n"は(最終的に)"\x0d\x0a"に変換される
> echo A| perl -ne "printf(qq{%v02X\n}, $_)"
41.0A
> perl -e "print qq{A\n}" | hexdump
00000000: 41 0D 0A                -                         |A  |
00000003;
> perl -e "die qq{A\n}" 2>&1 | hexdump
00000000: 41 0D 0A                -                         |A  |
00000003;

(hexdumpはgnuwin32に含まれる物を使っています。)

しかし、encodingモジュールを使うと標準入力と標準出力のエンコーディングが影響を受け、標準入力と標準出力の改行の変換が働かなくなります(仕様かどうかは分かりません)。

  • 標準入力で"\x0d\x0a"はそのまま読み込まれる
  • 標準出力で"\n""\x0a"に変換される
  • 標準エラーはencodingモジュールに関係しないので影響を受けない
> echo A| perl -Mencoding="cp932" -ne "printf(qq{%v02X\n}, $_)"
41.0D.0A
> perl -Mencoding="cp932" -e "print qq{A\n}" | hexdump
00000000: 41 0A                   -                         |A |
00000002;
> perl -Mencoding="cp932" -e "die qq{A\n}" 2>&1 | hexdump
00000000: 41 0D 0A                -                         |A  |
00000003;

改行の変換が働かなくなるのを防ぐにはbinmodeで改行コードを再設定するか、encodingモジュールでの標準入力と標準出力の設定を無効にすることです。無効化した場合は、他のモジュールで設定します。

> echo A| perl -Mencoding="cp932" -ne "BEGIN{ binmode STDIN => ':crlf' } printf(qq{%v02X\n}, $_)"
41.0A
> perl -Mencoding="cp932" -e "BEGIN{ binmode STDOUT => ':crlf' } print qq{A\n}" | hexdump
00000000: 41 0D 0A                -                         |A  |
00000003;

スクリプトで書くと次のようになります。binmodeで設定しています。

use strict;
use warnings;
use encoding "cp932";
binmode STDIN => ':crlf';
binmode STDOUT => ':crlf';
binmode STDERR => ':encoding(cp932)';
...

encodingモジュールでの標準入力と標準出力の設定を無効化した場合は、他のモジュールで設定します。ここではopenプラグマを使ってファイルも同時に設定しています。

use strict;
use warnings;
use encoding "cp932", STDIN => undef, STDOUT => undef;
use open qw(:encoding(cp932) :std);
...

小飼弾氏のよれば、encodingモジュールはあくまでも古いスクリプトを救済するためのモジュールとのことなので、新規に書き下ろす場合はUTF8で書いて、utf8モジュールを使いましょうとのことです。