perlでUTF-16LE + BOM

稀に、ファイルをUTF-16LE + BOMで保存したいということがある。

そもそもBOMが付いている訳だから、エンディアンがどうであるかを気にする必然は無いと思うし、特定のエンディアンしか読めないのならUTF-16LEと言い切ってしまえば良い訳で、その場合BOMは無用の長物だ。とはいえ、そういうことがしたい場合もあるにはある。(Winとかそっち方面で…しくしく。)

今日は「そういう事がしたい場合」だった。そこでperlで変換スクリプトを書こうと思ったのだが、UTF-16LE + BOMってどう変換するのかがいまいち不明だ。そういうエンコードの指定がある訳ではなさそうだし。ということで、普通にUTF-16LEでencodeしてからBOMを付けることにした。

#!/usr/bin/perl
use strict;
use warnings;
use Encode;
use Encode::Guess;
use Getopt::Std;

my $opts = {}; getopt( 'I', $opts );
my $buffer = join '', <>;
my $enc = $opts->{I} ? $opts->{I} : do {
    my $e = guess_encoding( $buffer );
    ref $e ? $e : 'utf-8';
};
print pack('H*','fffe'), encode( 'utf-16-le', decode( $enc, $buffer ) );
exit;

BOM = Byte Order Markはファイル冒頭、最初の文字より前に入る特殊文字でFEFFが割り当てられているが、これがエンディアンによってFFFEになる(表現される)ことを利用して、エンディアンがどちらかを見分ける。

ということで、LEの場合は逆転させてfffeを頭に付けてあげればOKなはず。スクリプトではファイル内容を読んでEncode::Guessで推測すると言った処理もしている。(doとかちょっと気持ち悪いが…。)ただしこのコードだと最初にファイルを全部メモリ上に置くことになるので、大きいファイルを扱う場合はもう一工夫必要そう。

で結局のところ、正解はどんなんだろう。

cf. Encode::Unicode - search.cpan.org