Perl Perl_5
Perl プラグマ open ファイルハンドルのエンコーディングを指定する I/O (0x28a)

目次 - Perl Index
Theme
Perl について、復習を兼ねて断片的な情報を掲載して行く連載その 0x28a 回。
Perl で、ファイルハンドルに対する入出力 ( I/O レイヤ ) のエンコーディングを一括で設定可能なプラグマ「 open 」を確認する。
プラグマ「 open 」
演算子としての「 open 」は (0x44) 等で確認しましたが、今回はプラグマとしての「 open 」を確認します。
プラグマ「 open 」は PerlIO のレイヤを任意にセットするもので、ファイルハンドルのエンコーディング等いくつかの設定を行うことが出来ます。
公式な情報は open - perldoc.jp から確認出来ます。
また、「 プラグマ 」という用語については (0x15) 等で少し確認しています。
I/O レイヤ
プラグマ「 open 」は、I/O レイヤ ( as known as "disciplines" ) へのインターフェイスの 1 つとして機能するので、このプラグマのレキシカルスコープ内で行われる入力 ( Input ) と出力 ( Output ) の全てはプラグマ「 open 」で指定された設定をデフォルトのレイヤとして利用することになります。
しかしながら、例えば (0x44) で確認した「 3 引数の open 」等で利用するレイヤを明示的に指定した場合は、そこで指定された設定が優先されます。
出力「 OUT 」
サブプラグマ「 OUT 」とタグ「 :encoding() 」を利用して、出力のデータストリームを「 Shift_JIS 」として扱うように指定してみます。
use open OUT => ":encoding(shiftjis)";
open my $fh, ">", "txt_sjis"
print $fh "こんにちは\n";
print $fh "さようなら\n";
close $fh;
上記を実行すると、プログラムを実行した現在のディレクトリに「 txt_sjis 」という名前のファイルが作成されます。
これをコマンド「 nkf 」のオプション「 -g 」でチェックすれば、次のようにファイルの文字コードを知ることが出来ます。
$ nkf -g txt_sjis
Shift_JIS (LF)
プラグマ「 open 」で指定した文字コード「 Shift_JIS 」が適用されていることがわかります。
今度は、文字コードの指定を「 UTF-8 」にしてみます。
use open OUT => ':encoding(utf-8)';
open my $fh, ">", "txt_utf8" or die "$!";
print $fh "こんにちは\n";
print $fh "さようなら\n";
close $fh;
このプログラムを実行して作成されるファイル「 txt_utf8 」をコマンド「 nkf 」でチェックします。
$ nkf -g txt_utf8
UTF-8
文字コードが「 UTF-8 」のファイルが作成されたことがわかります。
これらは、次のように「 3 引数の open 」で個別に指定したことと同じです。
open $fh, '>:encoding(UFF-8)', "txt_utf8" or die "$!";
入力「 IN 」
サブプラグマ「 IN 」とタグ「 :encoding() 」を利用して、入力のデータストリームのコードを「 Shift_JIS 」として扱うように指定してみます。
use open IN => ':encoding(shiftjis)';
open my $fh, "<", "txt_sjis" or die "$!";
my @line = <$fh>;
close $fh;
これによって、「 Shift_JIS 」で「 こんにちは\nさようなら\n 」と記述されたファイル「 txt_sjis 」から読み込んだデータは、「 Shift_JIS 」として正しく解釈され、内部文字列「 flagged utf8 」に正しく変換されます。
データの構造を覗き見出来るモジュール「 Devel::Peek 」を利用すれば、文字列に utf8 フラグが立ち「 flagged utf8 」として扱われていることがわかります ( see Perl の文字列エンコーディングの話 - Hachioji.pm )。
use Devel::Peek;
Devel::Peek::Dump($_) for @line;
次の結果が得られます。
SV = PV(0x6cf398) at 0x6d0b98
REFCNT = 2
FLAGS = (POK,pPOK,UTF8) # utf8 フラグ
PV = 0x78f710 "\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257\n"\0 [UTF8 "\x{3053}\x{3093}\x{306b}\x{3061}\x{306f}\n"]
CUR = 16
LEN = 24
SV = PV(0x6cf008) at 0x6fa190
REFCNT = 2
FLAGS = (POK,pPOK,UTF8) # utf8 フラグ
PV = 0x76fbb0 "\343\201\225\343\202\210\343\201\206\343\201\252\343\202\211\n"\0 [UTF8 "\x{3055}\x{3088}\x{3046}\x{306a}\x{3089}\n"]
CUR = 16
LEN = 24
ただし、(0x243) で確認した通り、内部文字列「 flagged utf8 」のままで文字列を出力すると「 Wide character in print at ... 」の警告が出てしまうので、例えばモジュール「 Encode 」のルーチン「 encode() 」等を利用して任意の文字コードにエンコードしてから出力します。
use Encode;
# UTF-8 にエンコードしてから出力
print map{ encode('utf-8', $_) }@line;
これによって次の結果が得られます。
こんにちは
さようなら
入出力「 IO 」
サブプラグマ「 IO 」を利用すれば、入力「 IN 」と出力「 OUT 」を一度に指定出来ます。
use open IO => ':encoding(utf-8)';
# UTF-8 として読み込み
open my $in, "<", "txt_utf8" or die "$!";
my @line = <$in>;
close $in;
# UTF-8 として書き込み
open my $out, ">", "txt_utf8_2" or die "$!";
print $out "$_" for @line;
close $out;
上記コードでは、プラグマ「 open 」で入出力のエンコーディングを「 UTF-8 」に指定してありますので、ファイル「 txt_utf8 」の読み込みとファイル「 txt_utf8_2 」への書き込みはともに「 UTF-8 」として処理されます。
ですから、文字列データが書き込まれたファイル「 txt_utf8_2 」をコマンド「 nkf -g 」で確認すると次のように文字コードが「 UTF-8 」になっていることが確認出来ます。
$ nkf -g txt_utf8_2
UTF-8
入出力間での処理
ファイルハンドルの入力ストリームから読み込まれたデータは、プラグマ「 open 」の指定によって正しく内部文字列「 flagged utf8 」に変換されているので、例えば、マルチバイト文字列の置換処理等も正しく行うことが出来ます。
use open IO => ':encoding(utf-8)';
open my $in, "<", "txt_utf8" or die "$!";
my @line = <$in>;
close $in;
# --- txt_utf8 ---
# こんにちは
# さようなら
open my $out, ">", "txt_utf8_2" or die "$!";
foreach my $line (@line){
# 置換処理をして書き込み
$line =~ s/にちは/ばんわ/;
print $out "$line";
}
close $out;
# --- txt_utf8_2 ---
# こんばんわ
# さようなら
つまりここでは、(0x244) で確認したマルチバイト文字列処理の心得「 入口で decode, 出口で encode 」をプラグマ「 open 」を利用して行っているわけです。
その他のサブプラグマ
プラグマ「 open 」のサブプラグマ「 IN 」, 「 OUT 」, 「 IO 」を確認しましたが、他にも (0x45) 等で確認した「 :crlf 」や「 :locale 」等いくつかのサブプラグマが利用可能です。
詳細は open - perldoc.jp や PerlIO - CPAN を確認します。
0x28a -> 0x28b へ
今回確認した内容は、perl - Encode 入門 - 404 Blog Not Found 辺りと併せて読むと理解が深まるかもしれません。
次回は、「 PerlIO 」のドキュメントを確認します。
参考情報は書籍「 初めての Perl 第 6 版 」を中心に perldoc, Wikipedia および各 Web サイト。それと詳しい先輩。
目次 - Perl Index
Perl mp2 翻訳 Web コンテンツ圧縮の FAQ (d228)
Perl mp2 翻訳 既知のブラウザのバグの回避策をいくつか (d227)
Perl mp2 翻訳 Perl と Apache でのキュートなトリック (d226)
Perl mp2 翻訳 テンプレートシステムの選択 (d225)
Perl mp2 翻訳 大規模 E コマースサイトの構築 (d224)
Perl mp2 翻訳 チュートリアル (d223)
Perl mp2 翻訳 既知のブラウザのバグの回避策をいくつか (d227)
Perl mp2 翻訳 Perl と Apache でのキュートなトリック (d226)
Perl mp2 翻訳 テンプレートシステムの選択 (d225)
Perl mp2 翻訳 大規模 E コマースサイトの構築 (d224)
Perl mp2 翻訳 チュートリアル (d223)