トップページ > com/net > [底]Perl で UTF-16 のファイルを書く

[底]Perl で UTF-16 のファイルを書く

 仕事で使う UTF-16 のファイルを Perl で処理するスクリプトの改修がようやく一段落しそうです。今年の2月に書いた記事「Perl で UTF-16 のファイルを読む」の続きです。

 前回は BOM 付き UTF-16 リトルエンディアン(長くて面倒なので以下 Encode の記述方式に倣って UTF16LE とします)の SGML ファイルを読み込んで UTF-8 のテキストを書き出すところまで実現できました。今回はその続きで、入力ファイルと同じ UTF16LE の SGML を書き出す部分です。前回の記事のスクリプトに、書き出し先の文を追加するだけです。

 まずは改修前のスクリプト。

use utf8;
use strict;
use Encode;
use Encode::Guess qw/ shiftjis /;
$in = <>; #入力ファイルはスクリプトと別に手入力
open TEST, '<:bytes', "$in" or die; #入力値をバイト列としてファイルを開く
my $le = 0;
my $testline = <TEST>;
$testline = unpack "H4", $testline; #先頭の4バイトを16進数の文字列として取得
$le = 1 if ($testline =~ m!fffe!); #先頭の4バイトが FFFE だったらフラグを1にする
close TEST;
if ($le) {
open IN, '<:encoding(utf16le)', "$in" or die; #フラグが1なら UTF-16 で開く
} else {
open IN, '<:encoding(shiftjis)', "$in" or die; #フラグが0なら Shift_JIS で開く
}
while ($line = <EXP>) {
$line =~ s!(¥x0d¥x0a|¥x¥d|¥x¥a)!¥n!g; #改行コードを全部 ¥n に変換
#行ごとの処理
}
close IN;

 これを改修した結果は以下のとおり。入力が UTF16LE と Shift_JIS の2とおりあるため、出力は入力と同じ文字コードで出力するようにしています。

use utf8;
use strict;
use Encode;
use Encode::Guess qw/ shiftjis /;
my $in = <>; #入力ファイルはスクリプトと別に手入力
my $execMode = <>; #出力モードを手入力
open TEST, '<:bytes', "$in" or die; #入力値をバイト列としてファイルを開く
my $le = 0;
my $testline = <TEST>;
$testline = unpack "H4", $testline; #先頭の4バイトを16進数の文字列として取得
$le = 1 if ($testline =~ m!fffe!); #先頭の4バイトが FFFE だったらフラグを1にする
close TEST;
if ($le) {
open IN, '<:encoding(utf16le)', "$in" or die; #フラグが1なら入力ファイルを UTF16LE で開く
if ($execMode =~ m!x$!i) { #出力を UTF16LE の SGML にする場合は……
open OUT, ">raw:encoding(utf16le):crlf", "$out" or die; #(1) 改行コードを指定できる形で開く
print OUT "¥x{FEFF}"; #(2) BOM を 16 進数で直接指定
print OUT "<root>¥n"; #ルートのタグを追加

} else { #出力を UTF-8 のテキストにする場合は……
open OUT, '>encoding(utf8)', "$out" or die;
}
} else {
open IN, '<:encoding(shiftjis)', "$in" or die; #フラグが0なら入力ファイルを Shift_JIS で開く
if ($execMode =~ m!x$!i) { #出力を Shift_JIS の SGML にする場合は
open OUT, ">encoding(shiftjis)", "$out" or die; #Shift_JIS で開く
print OUT "<root>¥n"; #ルートのタグを追加

} else { #出力を UTF-8 のテキストにする場合は……
open OUT, '>encoding(utf8)', "$out" or die;
}
}
while ($line = <EXP>) {
$line =~ s!(¥x0d¥x0a|¥x¥d|¥x¥a)!¥n!g; #改行コードを全部 ¥n に変換
#行ごとの処理
}
close IN;
#出力処理
print OUT "</root>" if ($execMode =~ m!x$!i);
close OUT;

 前回挫折したポイントが (1) の改行コード部分。これを単純に “>:encoding(utf16le)” とするだけでは出力がすべて文字化けしてしまうのです。そして今回地味に時間がかかったのが、(2) の BOM。

 上記の “>:raw:encoding(utf16le):crlf” の部分をクリアしたとしても、適切な BOM をファイルの先頭におかなければ、UTF16LE であるという判定ができないため、結果的に文字化けしてしまいます。ただし、単純に print “FFFE” (「FFFE」と出力) と書いてももちろんダメで、16進数としてファイルの先頭に書く必要があります。
 16進数をそのまま表すために \{hh} という表記方法があるのですが、私はそれを知らなかったために pack/unpack をひたすら読み込んで、文字コード判定時のコードを逆転させてなんとかならないかとトライアンドエラーを繰り返しました。完全に無駄な時間でしたが……。ともあれ、途中で pack/unpack ではどうにもならんと検索し直したら \x{hh} を発見して、一件落着。何故か FEFF と逆に記載していますが、タイプミスではなくこうしないとダメみたいです。

 しかし、ここまで来てしまうと全体のブラッシュアップをしたくなってきました。出力の処理を記述する部分がダラダラと長くて改修もしづらいし、作成当初は使っていたのですが今はもういらないモードもありますし……。しかしそれをするにはさすがに時間が足りないですね。とりあえず現状でも問題なく使えているので、そのうち、そのうち……。

コメント:0

コメントフォーム
この情報を記録しますか?

トラックバック:0

この記事のトラックバック URL
http://third.system.cx/wordpress/wp-trackback.php?p=269
トラックバック元のサイトへのリンク
[底]Perl で UTF-16 のファイルを書く from the third place -5th take-

トップページ > com/net > [底]Perl で UTF-16 のファイルを書く

サイト内検索
紹介

管理人:春屋アロヅ(alohz)
連絡先:alohzhgmail.com
詳細、サイトについては諸々の紹介から。

最近の記事
カテゴリー
昔の記事
外部コンテンツ

ページの先頭に戻る