バイナリーを扱う準備をする
binmode
は、ランタイムライブラリーがバイナリーファイルとテキストファイルを区別するようなシステム上で、
バイナリーモードまたはテキストモードで FILEHANDLE を通してデータを読み書きできるよう準備します。
binmode
は成功すると 1 を返し、失敗すると undef
を返します。
失敗時のエラー内容は $! にセットされます。
ランタイムライブラリーがバイナリーファイルとテキストファイルを区別するようなシステムとは、具体的には Windows のことです。
Windows でバイナリーデータを扱う場合は、binmode
を使って FILEHANDLE をバイナリーモードにセットする必要があります。
次のようにして、binmode
で FILEHANDLE をバイナリーモードにセットします。
open my $fh, '>', './sample.bin';
binmode $fh;
上記のコードに関して、UNIX OS などでは binmode
は何も作用しませんが、コードがどのシステムでも動作することを求めるなら、
バイナリーを扱う際には常に binmode
を呼び出すのが良いでしょう。
Windows で binmode
でバイナリーモードにせずにバイナリーデータを扱うと何が問題かというと、
改行コードの自動変換が行われてしまう点です。
例えば、ファイルにバイナリーデータを print
を使って書き込む際に、
LF (0A
) が CRLF (0D 0A
) に変換されて書き込まれてしまいます。
バイナリーファイルとして保存したい場合、この変換は致命的です。
ちょっとした実験をしてみましょう。Windows で次のように LF (0x0A) の 1 バイトだけを
print
を使ってファイルに書き込んでみます。
my $data = pack( 'C1', 0x0A );
open my $fh, '>', './sample.bin';
print $fh $data; # CRLF (0D 0A) の 2 バイトが書き込まれてしまう!!!
close $fh;
上記コードで生成された sample.bin
の中身を調べると、LF (0A
) の 1 バイトではなく、
CRLF (0D 0A
) の 2 バイトが書き込まれていることが分かります。
binmode
はファイルハンドルをバイナリーモードにセットするために使われることが多いのですが、
別のモードにセットすることもできます。
引数 LAYER は、省略された場合は ":raw"
がセットされたとして処理されます。
これはバイナリーモードを意味します。この LAYER には、他に ":utf8"
や
":encoding(UTF-8)"
を指定することもできます。
これは Perl 内部文字列と外部文字列の変換を自動で行ってくれることになるため、
状況によっては非常に重宝します。
例えば、日本語をコンソールに出力することを考えてみましょう。
コード内に記述された日本語をコンソールに出力するには、次のように内部文字列を外部文字列に変換する必要があります。
以下のコードでは utf8::encode()
を使って外部文字列に変換しています。
use utf8;
my $str = 'こんにちは';
utf8::encode($str);
print $str;
上記サンプルのように出力が 1 度だけならさほど手間もかかりませんが、
出力の処理が多ければコードを書くのが面倒です。
そこで binmode
を使って、標準出力 (STDOUT) のモードを
utf8 モードにしてしまうことができます。
use utf8;
binmode STDOUT, ":utf8";
my $str = 'こんにちは';
print $str;
一度、標準出力 (STDOUT) のモードを utf8 モードにしておけば、 以降、外部文字列への変換を明示的に行う必要が無くなります。 もちろん、この手法は標準出力だけでなくファイル出力にも使えます。