キーワード

プロフィール

深沢千尋

Author:深沢千尋
みなさんこんにちは、深沢千尋です。(公式ページ
文字コード【超】研究 改訂第2版NEW!」「すぐわかるPerl」「すぐわかる オブジェクト指向 Perl」の著者です。
ここでは、多くは技術的でないこと、ごくまれに技術的なことをなげやりに書いていきます。
メールは suguwakaruPerl@gmail.com まで。(アットマークは ASCII に)
Twitterはじめました。@query1000です。よろしく~

最新記事

最新コメント

最新トラックバック

月別アーカイブ

カテゴリ

検索フォーム

RSSリンクの表示

リンク

ブロとも申請フォーム

QRコード

QRコード

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

「まるごとEncode」解題(6)そして encode()

つづき。

以下のプログラムがあるとする。

#! perl
# noBinmode.pl -- decode して encode しないと怒られる
use strict;
use warnings;
use Encode;

my $utf8 = decode_utf8('abc漢字ひらがなカタカナ');
my $substr = substr($utf8, 5, 4);

print $substr;

__END__

実行してみる。(UTF-8 出力されるからリダイレクトで)

F:\Dropbox20091029\My Dropbox\Marugoto>noBinmode.pl > k.txt
Wide character in print at F:\Dropbox20091029\My Dropbox\Marugoto\noBinmode.pl line 12.

k.txt には正しく

ひらがな

と入っているが、「ワイド文字を print しました」と怒られる。
これは、既出だが、Perl 内部表現の文字列をオクテットストリームに変換しないでそのまま print したときのエラーである。
この場合は、binmode を使うか、Encode::decode_utf8() の逆関数 Encode::encode_utf8() を使う。

ここでは、encode_utf8(); を使ってみる。

#! perl
# expEncode.pl -- encode の実験
use strict;
use warnings;
use Encode;

my $utf8 = decode_utf8('abc漢字ひらがなカタカナ');
my $substr = encode_utf8(substr($utf8, 5, 4));

print $substr;

__END__

実行。

F:\Dropbox20091029\My Dropbox\Marugoto>expEncode.pl > k.txt

エラーが消えた。

無論、encode('UTF-8', ...) としても一緒である。

#! perl
# expEncode.pl -- encode の実験
use strict;
use warnings;
use Encode;

my $utf8 = decode_utf8('abc漢字ひらがなカタカナ');
my $substr = substr($utf8, 5, 4);

print encode("UTF-8", $substr);

__END__

今度はリダイレクトしないで実行してみると

F:\Dropbox20091029\My Dropbox\Marugoto>expEncode.pl
ゅ・繧峨′縺ェ

昔は文字化けというと魚偏だったけど、最近はこの糸偏が多い。
UTF-8 文字を強制的に Shift_JIS で解釈するとこのように怒られる。
ここで "UTF-8" を好みの文字コードに変えられる。

#! perl
# expEncode.pl -- encode の実験
use strict;
use warnings;
use Encode;

my $utf8 = decode_utf8('abc漢字ひらがなカタカナ');
my $substr = substr($utf8, 5, 4);

print encode("cp932", $substr);

__END__

F:\Dropbox20091029\My Dropbox\Marugoto>expEncode.pl
ひらがな

CP932 なので正しく DOS 窓に表示された。



さて、少し横道に逸れるが、現在スクリプトは UTF-8 になっているので、スクリプトに UTF-8 にはあるけれど、CP932 の文字レパートリーにない難しい字を書いた時にどうなるのかという問題がある。

#! perl
# expEncode.pl -- encode の実験
use strict;
use warnings;
use Encode;

#my $utf8 = decode_utf8('abc漢字ひらがなカタカナ');
my $utf8 = decode_utf8('abc漢字♠♥♦♣カタカナ');
my $substr = substr($utf8, 5, 4);

print encode("cp932", $substr);

__END__

いま、「ひらがな」の変わりに「♠♥♦♣」というトランプのマークを書いた。
これは、U+2660 らへんからある Unicode 文字である。

20091112tramps.png

CP932 にあるかどうか知らない。
試みにこのスクリプトを秀丸で言うところの「日本語(Shift-JIS)」で保存してみる。
(IANA 登録名は Shift-JISではなくて Shift_JIS であり、実際には DOS 窓のコードページは CP932 だが)
次のように怒られる。

20091112henkanErr.png

[はい] をクリックするとこうなる。

20091112hatena.png


あららー。
元通り、♠♥♦♣に直して、UTF-8で保存しておく。

20091112resaved.png

では実行する。

F:\Dropbox20091029\My Dropbox\Marugoto>expEncode.pl
????

? になって表示された。

これ、昨日 Dan Kogai さんじきじきに教わったのだが、encode に第3引数として 1 を渡せばよいという。

こうだ。

#! perl
# expEncode.pl -- encode の実験
use strict;
use warnings;
use Encode;

#my $utf8 = decode_utf8('abc漢字ひらがなカタカナ');
my $utf8 = decode_utf8('abc漢字♠♥♦♣カタカナ');
my $substr = substr($utf8, 5, 4);

print encode("cp932", $substr, 1);

__END__

実行する。

F:\Dropbox20091029\My Dropbox\Marugoto>expEncode.pl
"\x{2660}" does not map to cp932 at C:/strawberry/perl/lib/Encode.pm line 158.

あらホントだ~。
U+2660 は上に見たとおり確かにスペードである。

以下は参考ページ。
・http://blog.livedoor.jp/dankogai/archives/51047005.html

encode の第3引数については「まるごと」でも述べられており、よって本記事でも後述する。



ということで、基本はこう。

・処理の前に decode() -- オクテットストリームを Perl内部表現にする
・文字単位で処理
・処理の後に encode() -- Perl内部表現をオクテットストリームにする

「この基本を覚えておけば、Encodeの使い方はマスターしたも同然です。」(原記事より)



さて、また上の記事に戻るが、やはり CP932 でスクリプトを書きたい人は存在するだろう。
この場合は use utf8; の代わりに use encodings "cp932"; と書けるのだが、これは Dan さんによって強く非推奨されている。

・http://blog.livedoor.jp/dankogai/archives/50857509.html
・http://blog.livedoor.jp/dankogai/archives/51221731.html

表示の表やソ連のソの後半に \5c が入っていて、Perl は他にも @ とかいろいろぶつかるから、プログラムは EUC-JP で書いて入出力は nkf をかます、とか昔から言ってたのだが、今はプログラムは UTF-8 で書いて use utf8; し、外部からの入力は decode、外部への出力は encode するというのが正しいあり方のよう。

ただ、これに問題があるのが、Windows で使うプログラムを UTF-8 で書くと CP932 にない漢字(♠♥♦♣)を書いちゃうのではないか、ということなのだが、Dan さんによると、encode の第3引数に 1 を渡すと、croak してくれるという。
これは上でも見たがすごく便利。

でも、
 ・プログラムを書いた瞬間に怒られたい気がする
   <=これは何らかのハックで、CP932 チェッカー的なものを保存時に走らせればいいだけかも。
Emacs とかだったらカンタンにできるのだが・・・。

 ・encode ではなくて binmode でできないだろうか?
   <=とりあえず binmode に第3引数が「ない」ことは確かのよう。
これは今後の研究課題

今回は以上。
スポンサーサイト

「まるごとEncode」解題(5)今さら decode() の解説されても・・・

つづき。

以下のプログラムを実行する。

#! perl
# decodeExp.pl -- decode を使って UTF-8 文字を作る
use strict;
use warnings;
use Encode;

binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更

#6文字目のひから4文字、ひらがなと表示される

my $bytes = 'abc漢字ひらがなカタカナ';
print "bytes:", decode('utf8', substr($bytes, 9, 12)), "\n";

my $utf8 = decode('UTF-8', $bytes);
print "utf8:", substr($utf8, 5, 4), "\n";

__END__

以下のように出力される。

F:\Dropbox20091029\My Dropbox\Marugoto>decodeExp.pl
bytes:ひらがな
utf8:ひらがな

これは(もうさんざん出てきてしまったが)decode 関数を説明するプログラムである。

本プログラムは、use utf8; プラグマに入っていないので、文字列リテラルが Perl 内部表現である。
ために、

'abc漢字ひらがなカタカナ'

から「ひらがな」の部分を取り出すには、

substr($bytes, 9, 12)

と書く必要がある。
「abc」は ASCII だから 1 文字 1 バイトだから全部で 3 バイト。
「漢字」は CJK 統合漢字(基本)だから1文字3バイトだから全部で 6 バイト。
ここまでが9バイト。
だから「ひらがな」は 10 バイト目から始まる。
「ひらがな」はひらがなだけど CJK 統合漢字(基本)だから1文字3バイトだから全部で12バイト。
よって10バイト目から12バイト、と数える必要があった。

しかしながら、

my $utf8 = decode('UTF-8', $bytes);

と書くことによって、バイトストリーム文字列 $bytes が Perl 内部表現に変わって $utf8 になることで、

print "utf8:", substr($utf8, 5, 4), "\n";

で(6文字目から4文字で)「ひらがな」が取り出せる。
グッとカンタン。
JPerlみたいだ!←

しかしながら、その前に、

print "bytes:", decode('utf8', substr($bytes, 9, 12)), "\n";

でもう decode が出てきてしまっているではないか。
これは、binmode で cp932 を指定してるが、ここで encode されるのに備えて decode しているのだ。
(Perl 内部表現でないと binmode で cp932 に変更できないから。)

まとめると、
・原記事は UTF-8 出力前提だが、
・それを DOS 窓前提にすると、
・プログラム本体を UTF-8 で書くために、
・出力するときには binmode STDOUT, (:cp932); を使わざるを得なくなって、
・binmode は encode するから、
・プログラム本体が use utf8; の場合は、
・文字列がバイトストリームなので、
・print 前に decode が必要
という話だ。
ああめんどくさい。

decode の第1引数の UTF-8 と utf8 は、http://perldoc.jp/docs/perl/5.10.0/perlunifaq.pod ではこう述べられている。

What's the difference between UTF-8 and utf8?
(UTF-8 と utf8 の違いは?)

UTF-8 は公式な標準です。 utf8 は、何を受け入れるかに関して自由な Perl のやり方です。もしそれほど自由でないものと対話する必要があるなら、 UTF-8 を使うことを考えたくなるかもしれません。自由すぎるものと対話する必要があるなら、utf8 を使わなければならないかもしれません。完全な説明は Encode にあります。

UTF-8 は内部では utf-8-strict として知られます。チュートリアルでは、たとえ内部では実際には utf8 が使われる場合でも一貫して UTF-8 を使っています; なぜなら区別をつけるのは難しく、ほとんど無意味だからです。

例えば utf8 は、9999999 のような、Unicode に存在しない符号位置も使えますが、これを UTF-8 でエンコードすると、代替文字を得ることになります(これはデフォルトの場合です; これを扱う他の方法については Encode/"Handling Malformed Data" を参照してください。)

わかりました、どうしてもと言うのなら:「内部形式」は utf8 であって、 UTF-8 ではありません。 (もしその他のエンコーディングでないのなら。)


さて、$bytes と、それを decode した $utf8 についてさらに調べる。
Devel::Peek::Dump というメソッドを使うと、変数の UTF8 フラグを調べることができる。
Devel は developper(開発者用)、Peek は覗くという意味だ。

#! perl
# expPeek.pl -- Devel::Peek::Dump を使って UTF8 フラグを覗く
use strict;
use warnings;
use Encode;
use Devel::Peek;

binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更

#6文字目の「ひ」から4文字、ひらがなと表示される

my $bytes = 'abc漢字ひらがなカタカナ';
Dump $bytes;
print "bytes:", decode('utf8', substr($bytes, 9, 12)), "\n";

my $utf8 = decode('UTF-8', $bytes);
Dump $utf8;
print "utf8:", substr($utf8, 5, 4), "\n";

__END__

実行する。

C:\Marugoto>expPeek.pl
SV = PV(0x3d6a94) at 0x9d9cc4
REFCNT = 1
FLAGS = (PADMY,POK,pPOK)
PV = 0x9e0fbc "abc\346\274\242\345\255\227\343\201\262\343\202\211\343\201\214
\343\201\252\343\202\253\343\202\277\343\202\253\343\203\212"\0
CUR = 33
LEN = 36
bytes:ひらがな
SV = PV(0x3d6c0c) at 0x9ef264
REFCNT = 1
FLAGS = (PADMY,POK,pPOK,UTF8)
PV = 0xa6ee4c "abc\346\274\242\345\255\227\343\201\262\343\202\211\343\201\214
\343\201\252\343\202\253\343\202\277\343\202\253\343\203\212"\0 [UTF8 "abc\x{6f2
2}\x{5b57}\x{3072}\x{3089}\x{304c}\x{306a}\x{30ab}\x{30bf}\x{30ab}\x{30ca}"]
CUR = 33
LEN = 36
utf8:ひらがな

こういうの見るとお目目が痛くなるが、ポイントは、
最初の Dump $bytes; は
FLAGS = (PADMY,POK,pPOK)
と、表示し、2回目の Dump $utf8; は
FLAGS = (PADMY,POK,pPOK,UTF8)
と表示していること。
これにより、decode によって UTF8 フラグが付与されたことが分かる。
UTF8 フラグが付与されると、substr が文字単位で動作する。
(他にもいいことはいっぱいあると思うが~)

なお、UTF8 フラグは、別に中身が UTF-8 でエンコーディングされてるからそういう名前なのではない。

上のプログラムを以下のように変更する。

#! perl
# expPeek.pl -- Devel::Peek::Dump を使って UTF8 フラグを覗く
# Shift_JIS 版:CP932 で保存のこと

use strict;
use warnings;
use Encode;
use Devel::Peek;

binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更

#6文字目のひから4文字、ひらがなと表示される

my $bytes = 'abc漢字ひらがなカタカナ';
Dump $bytes;
print "bytes:", decode('shiftjis', substr($bytes, 7, 8)), "\n";

my $utf8 = decode('shiftjis', $bytes);
Dump $utf8;
print "utf8:", substr($utf8, 5, 4), "\n";

__END__

decode の第一引数を shiftjis に変えてみた。
プログラムも Shift_JIS(CP932)で保存した。
そうすると、1個目の substr の引数は8バイト目から8バイトになるから、そうした。
では実行。

C:\Documents and Settings\fuc\My Documents\My Dropbox\Marugoto>expPeek.pl
SV = PV(0x3d698c) at 0x9d9b84
REFCNT = 1
FLAGS = (PADMY,POK,pPOK)
PV = 0xaad824 "abc\212\277\216\232\202\320\202\347\202\252\202\310\203J\203^\2
03J\203i"\0
CUR = 23
LEN = 24
bytes:ひらがな
SV = PV(0xa73e64) at 0x9ef264
REFCNT = 1
FLAGS = (PADMY,POK,pPOK,UTF8)
PV = 0xa6ef5c "abc\346\274\242\345\255\227\343\201\262\343\202\211\343\201\214
\343\201\252\343\202\253\343\202\277\343\202\253\343\203\212"\0 [UTF8 "abc\x{6f2
2}\x{5b57}\x{3072}\x{3089}\x{304c}\x{306a}\x{30ab}\x{30bf}\x{30ab}\x{30ca}"]
CUR = 33
LEN = 36
utf8:ひらがな

この場合も、FLAGS についたフラグは UTF8 であって、SHIFTJIS とか CP932 ではないのがわかる。

しかしながら、CP932 でプログラムを書くとなにかと面倒が多い。
有名なのが表示の表やソ連のソの後半が(ってよく言っていたのだが、ソ連ってずいぶん前に崩壊したな・・・)「\」とカブる 0x5c 問題だが、Perl の場合は他にも @ とかがカブるので "" が怖くて使えなくなる。
昔は EUC-JP で作れと言われているが、今様は UTF-8 を使うようだ。

ということでプログラムを元に戻して UTF-8 で保存し直しておく。

さて、UTF8 フラグの立ち座りを調べるだけであれば、Devel::Peek::Dump を使わなくても utf8::is_utf8 というのを使えばよい。
これは真偽値を返す。

#! perl
# expIsUtf8.pll -- is_utf8 を使って UTF8 フラグを調べる
use strict;
use warnings;
use Encode;

binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更

#6文字目のひから4文字、ひらがなと表示される
my $bytes = 'abc漢字ひらがなカタカナ';
print "bytes:", decode('utf8', substr($bytes, 9, 12)), " is_utf:", utf8::is_utf8($bytes), "\n";

my $utf8 = decode('UTF-8', $bytes);
print "utf8:", substr($utf8, 5, 4), " is_utf:", utf8::is_utf8($utf8), "\n";

__END__

最初に use utf8; をするとプログラム全体の挙動が変わってしまうので、上では is_utf8 メソッドを呼ぶために

実行。

C:\Marugoto>expIsUtf8.pl
bytes:ひらがな is_utf:
utf8:ひらがな is_utf:1

上では、decode されていると 1 が、そうでないと NULL が返っているようだが、無論それを利用してプログラムを書くのではなく、

 decode 'utf8', $bytes if utf8::is_utf $bytes;

的に真偽値として使うべきだ。

なお、この場合 UTF8 フラグの有無しか気にしていないのだから、

・プログラムが UTF-8 で書かれているときは
  decode('UTF-8', $bytes)

・プログラムが Shift_JIS で書かれているときは
  decode('shiftjis', $bytes)

といちいち第1引数を渡すのは面倒だ。
省略する方法はないだろうか。

UTF-8 の場合は、decode_utf8 というのを使うと省略できる。
下のプログラムを UTF-8 で保存して動かすと動く。

#! perl
# expIsUtf8.pll -- is_utf8 を使って UTF8 フラグを調べる
use strict;
use warnings;
use Encode;

binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更

#6文字目のひから4文字、ひらがなと表示される
my $bytes = 'abc漢字ひらがなカタカナ';
print "bytes:", decode_utf8(substr($bytes, 9, 12)), " is_utf:", utf8::is_utf8($bytes), "\n";

my $utf8 = decode_utf8 $bytes;
print "utf8:", substr($utf8, 5, 4), " is_utf:", utf8::is_utf8($utf8), "\n";

__END__

F:\Dropbox20091029\My Dropbox\Marugoto>expIsUtf8.pl
bytes:ひらがな is_utf:
utf8:ひらがな is_utf:1

でも、次のようなプログラムを Shift_JIS で動かすと怒られる。

#! perl
# expIsUtf8.pll -- is_utf8 を使って UTF8 フラグを調べる
use strict;
use warnings;
use Encode;

binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更

#6文字目のひから4文字、ひらがなと表示される
my $bytes = 'abc漢字ひらがなカタカナ';
print "bytes:", decode_utf8(substr($bytes, 7, 8)), " is_utf:", utf8::is_utf8($bytes), "\n";

my $utf8 = decode_utf8 $bytes;
print "utf8:", substr($utf8, 5, 4), " is_utf:", utf8::is_utf8($utf8), "\n";

__END__

F:\Dropbox20091029\My Dropbox\Marugoto>expIsUtf8_sjis.pl
"\x{fffd}" does not map to cp932 at F:\Dropbox20091029\My Dropbox\Marugoto\expIsUtf8_sjis.pl line 11.
"\x{0402}" does not map to cp932 at F:\Dropbox20091029\My Dropbox\Marugoto\expIsUtf8_sjis.pl line 11.
"\x{70aa}" does not map to cp932 at F:\Dropbox20091029\My Dropbox\Marugoto\expIsUtf8_sjis.pl line 11.
"\x{fffd}" does not map to cp932 at F:\Dropbox20091029\My Dropbox\Marugoto\expIsUtf8_sjis.pl line 11.
"\x{fffd}" does not map to cp932 at F:\Dropbox20091029\My Dropbox\Marugoto\expIsUtf8_sjis.pl line 11.
bytes:\x{fffd}\x{0402}\x{70aa}\x{fffd}\x{fffd} is_utf:
"\x{fffd}" does not map to cp932 at F:\Dropbox20091029\My Dropbox\Marugoto\expIsUtf8_sjis.pl line 14.
"\x{fffd}" does not map to cp932 at F:\Dropbox20091029\My Dropbox\Marugoto\expIsUtf8_sjis.pl line 14.
"\x{fffd}" does not map to cp932 at F:\Dropbox20091029\My Dropbox\Marugoto\expIsUtf8_sjis.pl line 14.
"\x{0402}" does not map to cp932 at F:\Dropbox20091029\My Dropbox\Marugoto\expIsUtf8_sjis.pl line 14.
utf8:\x{fffd}\x{fffd}\x{fffd}\x{0402} is_utf:1

perldoc Encode によると、

$string = decode_utf8($octets [, CHECK]);
equivalent to $string = decode("utf8", $octets [, CHECK]) . The sequence of octets represented by $octets is decoded from UTF-8 into a sequence of logical characters. Not all sequences of octets form valid UTF-8 encodings, so it is possible for this call to fail. For CHECK, see "Handling Malformed Data".

ということだから、文字列から UTF8 フラグを倒すだけではなく、UTF-8 に変換してしまう。
\x{fffd} というのは、存在しない UTF-8 文字で、不当な UTF-8 文字を変換しようとしたので入れたものである。

他にも、utf8::decode とか Encode::_utf8_on というのもあったが、文字列が Shift_JIS だとうまくいかない。
やっぱり Perl(5.8 以降)の文字列は UTF-8 で書いておけということか。

なお、utf8::decode や Encode::_utf8_on は、Encode::decode_utf8 と違って不当な UTF-8 文字列をチェックしない。

あと、Encode::_utf8_on は、use Encode; していても Encode:: を付けて完全修飾しないといけないようだ。
[INTERNAL] だからかしら。

ていうか、http://perldoc.jp/docs/perl/5.10.0/perlunifaq.pod では UTF8 フラグは気にするな、is_utf8 は使うなと書いてあるが、そう言われると気になるのが人間というものね。

参考:
 ・http://blog.livedoor.jp/dankogai/archives/51004472.html
 ・http://blog.livedoor.jp/dankogai/archives/51290188.html

http://blog.livedoor.jp/dankogai/archives/51184112.html は勉強になる。
よくある「不当な Unicode 文字列をわざと書いて XSS 攻撃をする」というのを防ぐ方法が書いてある。

「まるごとEncode」解題(4)use bytes; の迷宮

つづき。

原記事から引用。
「このレキシカルプラグマ(use utf8;)は「ここから先、文字列リテラルは UTF-8 ですよ」ということを示しているのです。そうでない「下位互換モード」を強制するにはどうしたらよいかというと、「use bytes;」を使います。」
ということで、use bytes; の使い方を練習する。

まず、原記事に載っている同様、UTF-8 を出力するプログラムを動かしてみる。
文字列、注釈等は変えている。

#! perl
# bytesExp.pl -- use bytes を使ってみる

use strict;
use warnings;

# ブロックの外側ではUTF-8文字単位で解釈される
use utf8;

{
# ブロックの内側ではバイトモードが強制される(ようにしたい)
use bytes;
my $text = 'abc漢字ひらがなカタカナ';
print "inside:", substr($text, 9, 12)), "\n";
# 10バイト目から12バイト、ひらがなと表示されたい
}

my $text = 'abc漢字ひらがなカタカナ';
binmode STDOUT, ":utf8";
print "outside:", substr($text, 5, 4), "\n";
# 6文字目から4文字、ひらがなと表示されたい

__END__

実行。

C:\Documents and Settings\you\デスクトップ\Marugoto>bytesExp.pl
inside:縺イ繧峨′縺ェ
outside:縺イ繧峨′縺ェ

ぼくぐらいになってくるとこれぐらいの UTF-8 が CP932に化けているのは読めるようになってくるが(ウソ)これはきっと成功している。
ファイルにリダイレクトして中身を UTF-8 で見ると、ちゃんと

inside:ひらがな
outside:ひらがな

を得られた。

さて、上のプログラムでは、下の、use bytes が効いていない print には

binmode STDOUT, ":utf8";
print "outside:", substr($text, 5, 4), "\n";

と binmode を使っている。
これは、use bytes; スコープの外の substr は Perl 内部表現を返すので encode してやる必要があるからだ。

しかるに、ブロックの中の print は binmode をしていない。
print "inside:", substr($text, 9, 12)), "\n";

これは、use bytes; 影響下の substr はバイトストリームを返すので encode する必要はないからである。

では、同じプログラムを Windows で動かしてみようとすると、これがうまくいかない。

#! perl
# bytesExp.pl -- use bytes を使ってみる

use strict;
use warnings;
binmode STDOUT, ':encoding(cp932)';

# ブロックの外側ではUTF-8文字単位で解釈される
use utf8;

{
# ブロックの内側ではバイトモードが強制される(ようにしたい)
use bytes;
my $text = 'abc漢字ひらがなカタカナ';
print "inside:", Encode::decode('UTF-8', substr($text, 9, 12)), "\n";
# 10バイト目から12バイト、ひらがなと表示されたい
}

my $text = 'abc漢字ひらがなカタカナ';
print "outside:", substr($text, 5, 4), "\n";
# 6文字目から4文字、ひらがなと表示されたい

__END__

実行してみる。

C:\Documents and Settings\you\デスクトップ\Marugoto>bytesExp.pl
Cannot decode string with wide characters at C:/strawberry/perl/lib/Encode.pm line 174.

えっ174行もプログラム書いたっけ~ と思ったけど、これは decode を実行する Encode モジュールの中でコケている。
decode は以下の行で呼んでいる。

print "inside:", Encode::decode('UTF-8', substr($text, 5, 4)), "\n"; # ひらがなと表示されたい

これは例によって binmode で cp932 に切り替えて print 出力しているのだが、そうするとそのとき encode が起きるので、その前に decode してみた。
(ちなみに今回は use Encode; せず、Encode::decode と書いてみた。)

なぜ decode したかというと、use bytes; 配下にある文字列はバイトストリームであると思っていたからだ。
しかるに、上のエラーメッセージはなんだろうか。

Cannot decode string with wide characters at C:/strawberry/perl/lib/Encode.pm line 174.

ワイド文字をデコードできない、と言ってきている。
ワイド文字とは何か。
これは、第2回で、

use utf8; # スクリプトは UTF-8 で保存
...
#binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更
...
print substr($text, 5, 4);

というプログラムが print で怒られたときと同じである。
(binmode は怒られる実験のために注釈化)
utf8 配下の Perl 内部表現文字列を、binmodeなしでそのまま出力しようとしたので、あらワイド文字ね、と怒られたのだ。

とすると上の

binmode STDOUT, ':encoding(cp932)';
use utf8;
{
use bytes;
my $text = 'abc漢字ひらがなカタカナ';
print "inside:", Encode::decode('UTF-8', substr($text, 5, 4)), "\n"; # ひらがなと表示されたい

はどこがダメなんだろうか。

ここでは use bytes を use utf8 の取り消しのように使っているが、use utf8 の取り消しは no utf8 であって、use bytes はもうひとつ別の何か違うもののようである。

ではなんだろうか。
perldocに聞いてみる。

C:\Documents and Settings\you\デスクトップ\Marugoto>perldoc bytes # 以下、翻訳は深沢
SYNOPSIS 概要
use bytes;
... chr(...); # or bytes::chr
... index(...); # or bytes::index
... length(...); # or bytes::length
... ord(...); # or bytes::ord
... rindex(...); # or bytes::rindex
... substr(...); # or bytes::substr
no bytes;

DESCRIPTION 説明
The "use bytes" pragma disables character semantics for the rest of the
lexical scope in which it appears. "no bytes" can be used to reverse the
effect of "use bytes" within the current lexical scope.

"use bytes" プラグ間はこれが現れたレキシカルスコープの残りの部分において、
文字セマンティクスを無効化するものである。"no bytes" はそのカレント
スコープにおいて、"use bytes" の効果を打ち消すことができる。

Perl normally assumes character semantics in the presence of character
data (i.e. data that has come from a source that has been marked as
being of a particular character encoding). When "use bytes" is in
effect, the encoding is temporarily ignored, and each string is treated
as a series of bytes.

Perl は通常、存在する文字データ(すなわち、ソースから得られた文字列であって、
特定の文字エンコーディングにマークされているもの)は文字セマンティクスを仮定する。
"use bytes" が有効な場合、エンコーディングは一時的に無視され、個々の文字列は
バイトの連なりとして扱われる。

つまり、これは、文字列の Perl 内部表現性を変えるのではなく、それを扱う以下の関数

use bytes;
... chr(...); # or bytes::chr
... index(...); # or bytes::index
... length(...); # or bytes::length
... ord(...); # or bytes::ord
... rindex(...); # or bytes::rindex
... substr(...); # or bytes::substr
no bytes;

の挙動を変えるものだったのだ。

ということで、ブロック内も文字列は Perl 内部表現であり続けるようなので decode を取ってみる。

#! perl
# bytesExp.pl -- use bytes を使ってみる

use strict;
use warnings;
binmode STDOUT, ':encoding(cp932)';

# ブロックの外側ではUTF-8文字単位で解釈される
use utf8;

{
# ブロックの内側ではバイトモードが強制される(ようにしたい)
use bytes;
my $text = 'abc漢字ひらがなカタカナ';
print "inside:", substr($text, 9, 12), "\n";
# 10バイト目から12バイト、ひらがなと表示されたい
}

my $text = 'abc漢字ひらがなカタカナ';
print "outside:", substr($text, 5, 4), "\n";
# 6文字目から4文字、ひらがなと表示されたい

__END__

どうだろう。

C:\Documents and Settings\you\デスクトップ\Marugoto>bytesExp.pl
"\x{0081}" does not map to cp932 at C:\Documents and Settings\you\デスクトップ\Marugoto\bytesExp.pl line 17.
"\x{0082}" does not map to cp932 at C:\Documents and Settings\you\デスクトップ\Marugoto\bytesExp.pl line 17.
"\x{0089}" does not map to cp932 at C:\Documents and Settings\you\デスクトップ\Marugoto\bytesExp.pl line 17.
"\x{0081}" does not map to cp932 at C:\Documents and Settings\you\デスクトップ\Marugoto\bytesExp.pl line 17.
"\x{008c}" does not map to cp932 at C:\Documents and Settings\you\デスクトップ\Marugoto\bytesExp.pl line 17.
"\x{0081}" does not map to cp932 at C:\Documents and Settings\you\デスクトップ\Marugoto\bytesExp.pl line 17.
inside:a\x{0081}2a\x{0082}\x{0089}a\x{0081}\x{008c}a\x{0081}a
outside:ひらがな

うわっすごい怒られてしまった。

これは以前、

binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更
my $text = 'abc漢字ひらがなカタカナ';
print substr($text, 9, 12);

でやられたパターンと同じである。
何回も怒られていると怒られても動じなくなるね! (^^)

出力された文字は
a
\x{0081}
2
a
\x{0082}
\x{0089}
a
\x{0081}
\x{008c}
a
\x{0081}
a

で、これも UTF-8 の「ひらがな」が ISO 8859-1 として誤解釈されている。

やはりデコードしないといけないのか。

まとめると、

Encode::decode('UTF-8', substr($text, 9, 12))

だと decode に substr($text, 9, 12) はもう内部表現だからデコードできないよといわれ、

substr($text, 9, 12)

だと後続の print で ISO 8859-1 扱いを受けるという始末である。
どうすればいいのだろうか。

フト思い立って、substr の結果を一度 encode してから decode してみる。
こうだ。

#! perl
# bytesExp.pl -- use bytes を使ってみる

use strict;
use warnings;
binmode STDOUT, ':encoding(cp932)';

# ブロックの外側ではUTF-8文字単位で解釈される
use utf8;

{
# ブロックの内側ではバイトモードが強制される(ようにしたい)
use bytes;
my $text = 'abc漢字ひらがなカタカナ';
print "inside:", Encode::decode('UTF-8', Encode::encode("UTF-8", substr($text, 9, 12))), "\n";
# 10バイト目から12バイト、ひらがなと表示されたい
}

my $text = 'abc漢字ひらがなカタカナ';
print "outside:", substr($text, 5, 4), "\n";
# 6文字目から4文字、ひらがなと表示されたい

__END__

なんかもう、すげえよな。
では実行。

C:\Documents and Settings\you\デスクトップ\Marugoto>bytesExp.pl
inside:カタカナ
outside:ひらがな

ううん、見た目上エラーも出ず、何らかの文字列が表示されているが、ダメだ。

my $text = 'abc漢字ひらがなカタカナ';
print "inside:", Encode::decode('UTF-8', Encode::encode("UTF-8", substr($text, 9, 12))), "\n";

'abc漢字ひらがなカタカナ' は abc漢字の時点で9バイトで、ひらがなはそこから12バイトだ。
だからひらがなと出て欲しかったのだが、カタカナと出てしまった。
先頭から9バイトではなくて、9文字数えてしまっている。
つまり、bytesモードが効いていない。

そこで、perldoc で得られた知見を元に bytes::substr にしてみよう。

#! perl
# bytesExp.pl -- use bytes を使ってみる

use strict;
use warnings;
binmode STDOUT, ':encoding(cp932)';

# ブロックの外側ではUTF-8文字単位で解釈される
use utf8;

{
# ブロックの内側ではバイトモードが強制される(ようにしたい)
use bytes;
my $text = 'abc漢字ひらがなカタカナ';
print "inside:", Encode::decode('UTF-8', Encode::encode("UTF-8", bytes::substr($text, 9, 12))), "\n";
# 10バイト目から12バイト、ひらがなと表示されたい
}

my $text = 'abc漢字ひらがなカタカナ';
print "outside:", substr($text, 5, 4), "\n";
# 6文字目から4文字、ひらがなと表示されたい

__END__

実行。

C:\Documents and Settings\you\デスクトップ\Marugoto>bytesExp.pl
"\x{0081}" does not map to cp932 at C:\Documents and Settings\you\デスクトップ\Marugoto\bytesExp.pl line 15.
"\x{0082}" does not map to cp932 at C:\Documents and Settings\you\デスクトップ\Marugoto\bytesExp.pl line 15.
"\x{0089}" does not map to cp932 at C:\Documents and Settings\you\デスクトップ\Marugoto\bytesExp.pl line 15.
"\x{0081}" does not map to cp932 at C:\Documents and Settings\you\デスクトップ\Marugoto\bytesExp.pl line 15.
"\x{008c}" does not map to cp932 at C:\Documents and Settings\you\デスクトップ\Marugoto\bytesExp.pl line 15.
"\x{0081}" does not map to cp932 at C:\Documents and Settings\you\デスクトップ\Marugoto\bytesExp.pl line 15.
inside:a\x{0081}2a\x{0082}\x{0089}a\x{0081}\x{008c}a\x{0081}a
outside:ひらがな

もうわかった。
これは decode せずに encode したからだ。
現状はこうなっている。

print "inside:", Encode::decode('UTF-8', Encode::encode("UTF-8", bytes::substr($text, 9, 12))), "\n";

このうち、内側の Encode::encode は、substr がバイトストリームではなくて Perl 内部表現だという疑いの結果付けられたもので、それが bytes::substr である今、もう必要ないのではないか。
つまり、こうだ。

print "inside:", Encode::decode('UTF-8', bytes::substr($text, 9, 12)), "\n";

これで言ってみよう~

#! perl
# bytesExp.pl -- use bytes を使ってみる

use strict;
use warnings;
binmode STDOUT, ':encoding(cp932)';

# ブロックの外側ではUTF-8文字単位で解釈される
use utf8;

{
# ブロックの内側ではバイトモードが強制される(ようにしたい)
use bytes;
my $text = 'abc漢字ひらがなカタカナ';
print "inside:", Encode::decode('UTF-8', bytes::substr($text, 9, 12)), "\n";
# 10バイト目から12バイト、ひらがなと表示されたい
}

my $text = 'abc漢字ひらがなカタカナ';
print "outside:", substr($text, 5, 4), "\n";
# 6文字目から4文字、ひらがなと表示されたい

__END__

実行。

C:\Documents and Settings\you\デスクトップ\Marugoto>bytesExp.pl
inside:ひらがな
outside:ひらがな

アーうまくいった。
疲れた。
でも、せっかく use bytes; しているのに、なぜ bytes::substr を呼ばなければならないのか。
これでは意味がない。

最初の UTF-8 版では、このプログラムは UTF-8 で出力しているのでこうなっている。

use bytes;
my $text = 'abc漢字ひらがなカタカナ';
print "inside:", substr($text, 9, 12)), "\n";

このように、単純に substr の結果をそのまま print している。
しかるに Windows 版(CP932版)では

print "inside:", Encode::decode('UTF-8', bytes::substr($text, 9, 12)), "\n";

のように、bytes::substr を decode している。
これは、binmode で print 時に encode されるから、バイトストリームを Perl 内部表現に戻したのだ。

その前はこうしていた。

print "inside:", Encode::decode('UTF-8', Encode::encode("UTF-8", substr($text, 9, 12))), "\n";

substr が Perl 内部表現なので encode し、それを decode してから binmode で出力していた。
しかもこの substr は正しくバイトモードで起動せず、文字を数えていた。
わかりますか。

実は、現時点の use bytes; には疑問点があり、ある文字列関数Aが、別の関数Bの引数として渡されるとき、その関数Aには効果が及ばないようである。

次のプログラムでよくわかる。

#! perl
# expBytes.pl -- Enperiment of bytes
# 本件について OKWave でご教示をいただきました。お世話になりました。

use strict;
use warnings;
use utf8;
use bytes;
use Encode;

my $text = '漢字ひらがなカタカナEnglish';
print (Encode::is_utf8($text) ? "text is $text and utf8\n" : "text is $text and no utf8\n");

my $subtext = substr($text, 3, 3);
print (Encode::is_utf8($subtext) ? "subtext is $subtext and utf8\n" : "subtext is $subtext and no utf8\n");

my $subtext2 = returnStr(substr($text, 3, 3));
print (Encode::is_utf8($subtext2) ? "subtext2 is $subtext2 and utf8\n" : "subtext2 is $subtext2 and no utf8\n");

my $subtext3 = returnStr(bytes::substr($text, 3, 3));
print (Encode::is_utf8($subtext3) ? "subtext3 is $subtext3 and utf8\n" : "subtext3 is $subtext3 and no utf8\n");

sub returnStr {
shift
}

結果はこうなる。

text is 漢字ひらがなカタカナEnglish and utf8
subtext is 字 and no utf8
subtext2 is らがな and utf8
subtext3 is 字 and no utf8

Encode::is_utf8 はある文字列が Perl の内部表現かどうか調べるものだが、使用は推奨されていない。

さて、上記のプログラムはすべて use utf8; と use bytes; の配下である。

$text は Perl 内部表現である。

素の substr は 3, 3 を渡されて4バイト目から3バイトの「字」を返していて、出力 $subtext2 もバイトストリームである。

returnStr という何もしないラッパー関数でくるまれてしまった substr は 3, 3 を渡されて4文字目から3文字の「らがな」を返している。
しかも出力 $subtext3 は Perl 内部表現である。

同様に returnStr でくるまれた bytes::substr はバイトモードで動作して出力 $subtext3 もバイトストリームだ。

このように、substr は関数の引数になったとたんに、bytes が効かなくなる。
これは昨日 perlbug に起票してみた。

そうと分かれば最初のプログラムをもっと簡潔に改良でき。

#! perl
# bytesExp.pl -- use bytes を使ってみる

use strict;
use warnings;
binmode STDOUT, ':encoding(cp932)';

# ブロックの外側ではUTF-8文字単位で解釈される
use utf8;

{
# ブロックの内側ではバイトモードが強制される(ようにしたい)
use bytes;
my $text = 'abc漢字ひらがなカタカナ';

my $subtext = substr($text, 9, 12); # 一度変数に取ってみる

print "inside:", Encode::decode('UTF-8', $subtext), "\n";
# 10バイト目から12バイト、ひらがなと表示されたい
}

my $text = 'abc漢字ひらがなカタカナ';
print "outside:", substr($text, 5, 4), "\n";
# 6文字目から4文字、ひらがなと表示されたい

__END__

実行結果:

C:\Documents and Settings\you\デスクトップ\Marugoto>bytesExp.pl
inside:ひらがな
outside:ひらがな

substr の結果を直接 decode に渡さず、いったん変数に取ってから decode に渡している。
これで substr の挙動は無事 bytes 化され、10 バイト目から 12 バイトの「ひらがな」が返されるので、そのバイトストリームを decode して Perl 内部表現に変えてから print すると、binmode によって正しく CP932 で表示される。
めんどうだなあ。

ということで、use bytes が文字列関数をバイトモードで実行し、バイトストリームの結果を返すが、現在ちょっと不具合があって、別の関数の引数で使うと効かなくなってしまうということが分かった。

と、ここまで読んできてあっと驚きなされ、use bytes; は Encode::is_utf8 同様、非推奨機能だそうだ。
・バイトストリームが欲しければ encode する
・Perl 内部表現が欲しければ decode する
・UTF8 フラグの存在は忘れろ
だって。

perlunifaq - Perl Unicode FAQ
​http://perldoc.jp/docs/perl/5.10.0/perlunifaq.pod​

2008-06-25 - daily dayflower
​http://d.hatena.ne.jp/dayflower/20080625​

「まるごとEncode」解題(3)use utf8;のスコープ

つづき。

#! perl
# utfNo.pl -- UTF8モードとバイトモードの切り替え

use strict;
use warnings;
binmode STDOUT, ':encoding(cp932)'; # 出力は cp932

{
use utf8; # このブロックの中ではUTF-8文字単位で解釈される
my $text = 'abc漢字ひらがなカタカナ';

#6文字目から4文字表示
print substr($text, 5, 4);
}
{
# このブロックの中では use utf8; と書かれていない---オクテット単位で解釈される
my $text = 'abc漢字ひらがなカタカナ';

#10バイト目から12バイト表示。decode を忘れない
print Encode::decode('utf8', substr($text, 9, 12));
}
__END__

実行結果は以下の通り。

C:\Documents and Settings\you\デスクトップ\Marugoto>utfNo.pl
ひらがなひらがな

今度のプログラムのキモは

{
...
}
{
...
}

である。

use utf8; の効果はカーリーブレース { } で囲まれたブロックスコープで作用するので、ブロックを脱出すると効果がなくなる。
これを使って、前半は use utf8; プログラム、後半はバイトストリームプログラムにしている。

なお、use は no で打ち消されるので、

#! perl
# utfNo.pl -- UTF8モードとバイトモードの切り替え

use strict;
use warnings;
binmode STDOUT, ':encoding(cp932)'; # 出力は cp932

use utf8; # これ以降UTF-8文字単位で解釈される
my $text = 'abc漢字ひらがなカタカナ';

#6文字目から4文字表示
print substr($text, 5, 4);

no utf8; # これ以降オクテット単位で解釈される
$text = 'abc漢字ひらがなカタカナ';

#10バイト目から12バイト表示
print Encode::decode('utf8', substr($text, 9, 12));

__END__

と書いても同じこと。

この場合同じブロックで同じ $text を2回 my すると怒られるので注意。
(別に同じ変数を2回使えばいいから変数定義じたいいらないんだけど)

「まるごとEncode」(2)use utf8の導入

つづき。

#! perl
# newPerl.pl -- 日本語の文字を1文字として解釈
use strict;
use warnings;
use utf8; # スクリプトは UTF-8 で保存

binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更
# これがないと出力は UTF-8(縺イ繧峨′縺ェ)になるばかりか、
# Wide character in print といわれる。
# UTF-8 フラグがたっているからである。

my $text = 'abc漢字ひらがなカタカナ'; # 6文字目から4文字「ひらがな」と表示
print substr($text, 5, 4);

__END__

今度のプログラムのキモは

print substr($text, 5, 4);

である。

前回の

print decode('utf8', substr($text, 9, 12));

とは違い、

my $text = 'abc漢字ひらがなカタカナ'; # 6文字目から4文字「ひらがな」と表示

という文字をオクテット単位ではなくて、文字種に関係なく、文字単位で数えている。
(オクテット(octet)とは8ビットのことで、いわゆるバイトのこと。byte はコンピューターで扱う情報の最小単位のことで、オクテットの方が正確)

さて、

binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更

を指定しているのに、今回は print に渡す substr 関数の結果値を decode していない。
これは、$text が Perl 内部表現文字列だからである。

なぜかというと、

use utf8; # スクリプトは UTF-8 で保存

しているからだ。

これは、このプログラムを utf8 で書きますよという宣言だが、これによって文字列は Perl 内部表現になり、文字は文字単位で数えられる。
「abc」も「日本語」も3文字である。

ちなみに、

binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更

を外して実行してみると、

C:\Documents and Settings\you\デスクトップ\Marugoto>newPerl.pl
Wide character in print at C:\Documents and Settings\you\デスクトップ\Marugoto\newPerl.pl line 13.
縺イ繧峨′縺ェ

となる。

この「Wide character in print」というのは、ワイド文字が print されましたよ、ということだ。
ここでいうワイド文字というのは、encode していない文字のことだ。
つまり、Perl 内部表現文字をそのまま print してしまったときに発生するエラーである。
STDOUT を binmode してから print すると encode が行われ、正常にバイトストリーム化されるので、このエラーは出なくなる。

こころみに

binmode STDOUT, ':encoding(uff8)';

としてみるとよくわかる。

C:\Documents and Settings\you\デスクトップ\Marugoto>newPerl.pl
縺イ繧峨′縺ェ

難しい字はそのままだが、Wide character in print が消えた。

C:\Documents and Settings\you\デスクトップ\Marugoto>newPerl.pl > k.txt

とファイルにリダイレクトして中を見てみると、正しく UTF-8 の「ひらがな」になっている。

「まるごとEncode」解題(1)Encode以前のPerl(を説明するプログラムのはずが・・・)

2006年9月インプレス刊『まるごとPerl』の中の小飼 弾氏の記事「まるごとEncode」のコードを打ち込んで勉強しました。
以下に勉強の内容を書きます。
コードの内容は変えて、注釈も足してあります。
Dan さんの原記事や、この本全体は大変面白いので、ぜひそちらもお読みください。




かつての Perl は情報をオクテット単位で処理していたので、substr などの関数は、abcABC などの ASCII 文字は1文字ずつ処理していたが、「あいうえお」などのマルチバイト文字は、Shift_JIS の場合は半文字ずつ処理したり、UTF-8 の場合は 1/3 文字ずつ処理したりしていた。

#! perl
# oldPerl.pl -- かつてのPerl同様に、1オクテットを1文字として解釈
# UTF-8で保存して実行すること

use strict;
use warnings;
use Encode;

binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更

# use utf8; と書いていないので、オクテット単位で解釈される

my $text = 'abc漢字ひらがなカタカナ';

# UTF-8 だから「abc漢字」9オクテット使う

# decode して UTF-8 にしてやる
print decode('utf8', substr($text, 9, 12));

__END__

上のコードを実行すると以下のようになる。

C:\Documents and Settings\you\デスクトップ\Marugoto>oldPerl.pl
ひらがな



原記事は(おそらく UNIX 環境で)UTF-8 対応端末で実行するように書かれていたが、これを Strawberry Perl 5.10.1+Windows XP+コマンドプロンプト(以降は DOS 窓と呼ぶ)で実行した。
DOS 窓でも chcp 65001 で UTF-8 が表示されるはずだが、日本語を表示するためにレジストリをいじらなければならなく、ヘンな文字化けがいつまでたっても治らない。

上の Perl は、昔(5.5 時代)の Perl では文字列はオクテット単位だったんだよということを示すためのものだが、それを DOS 窓で表示するためにいろいろ新しい命令を使っている。

まず、

binmode STDOUT, ':encoding(cp932)'; # 出力はCP932に変更

で標準出力に出て行く文字コードを CP932 に切り替えている。
binmode は昔は Windows 用の Perl の改行モードを ASCII モードか Binary モードか切り替えていた。
最近の Perl(>= 5.6)では上のように文字コード系を指定できる。

あと、

print decode('utf8', substr($text, 9, 12));

も原記事では普通に

print substr($text, 9, 12);

だったが、こうすると

"\x{0081}" does not map to cp932.
"\x{0082}" does not map to cp932.
"\x{0089}" does not map to cp932.
"\x{0081}" does not map to cp932.
"\x{008c}" does not map to cp932.
"\x{0081}" does not map to cp932.
a\x{0081}2a\x{0082}\x{0089}a\x{0081}\x{008c}a\x{0081}a

となる。
出力された文字列を見ると、

a
\x{0081}
2
a
\x{0082}
\x{0089}
a
\x{0081}
\x{008c}
a
\x{0081}
a

と、12 文字/12 オクテットある。
これは、このスクリプトが use utf8; していないので ISO 8859-1 として解釈されていることがわかる。
(\x{0081}~\x{008c} は ISO 8859-1 にないので cp932 に変換できなくて怒られた)

binmode はこの文字を encode(Perl の内部表現からバイトストリームに変換)してから指定された文字コード系(ここでは cp932)に変換しようとする。
しかし、encode するためには文字列が Perl の内部表現でなければいけないので、decode を行う。

Perl の内部表現とは「文字列を文字列として扱うための特別な表現」である。
(実際には UTF8 フラグが立った UTF-8 列である。ただし、プログラマーが「UTF-8 であること」「UTF8 フラグが立ったり座ったりする」ことを意識することは推奨されていない。)

それに対してバイトストリームとはプログラムの外から入ってくるバイトの列である。

外界から入ってきたデータは decode して内部表現に変換し、内部表現を外界に出すためには encode してバイトストリームに変換する。

しかし、上のプログラムは use utf8; が書かれていないので、プログラムのリテラル文字列がバイトストリームである。
ところが、binmode を指定したファイルハンドル(ここでは STDOUT)への print を行うと、自動的に encode が行われる。
よって、その前に decode しておかなければならなかったわけである。

なお、

print decode('utf8', substr($text, 9, 12));

という風に書いているが、decode は Encode モジュールのメソッドであって、Perl の組み込み関数ではない。
これを使うために

use Encode;

を書いている。

・内部表現/バイトストリーム
・use utf8
・binmode
・decode/encode

以上の内容は、原記事ではこれ以降に順を追って出てくるが、上では DOS 窓で実行するために結局これらをすべて習得する羽目になってしまった。

テーマ : 雑記 - ジャンル : その他

[つぶやき]そうそう。ウィンクしてるのとそうじゃないのと大量にいる (^

10/27 10:55▽[つぶやき]台風一家: 10/21 22:41▽藤井さんと平野さんは鳩山さんが国民に用意した新たなパブリックエネミーか10/21 23:08▽ノムさん嫌いだったけどミーハーだから今年好きになった。来年クビになったらオリ近に.. http://bit.ly/2jlb6U

10/27 19:31▽大鵬の本名初めて知った。あの人確かロシア系。米朝は亭号なのになぜ大鵬は本名? RT @mainichijpedit 今年度の文化勲章、文化功労者が決まりました http://bit.ly/33alte 桂米朝さん、元横綱大鵬の納谷さんたちです

10/27 19:36▽あと「米朝会談 時期尚早」とかいうニュースを見ると落語家が誰と会談するの、しかもなぜこんな大見出しに?といつも思う

10/27 20:38▽ソフトウェアみたく使用許諾契約書が入ってて破くと同意したことになるともっとヤ(ウソが書いてても無保証とか言って) RT @dankogai 意見に対し御礼なうm(__)m。それでも漫画みたくシュリンクラップする時代が来たらやだな

10/27 23:14▽閣内の不一致 do you likeあの大臣が言ってる事orこの大臣が言ってること?

10/27 23:16▽「あの大臣まず建設中止ありきかよ、ダムイット!」

10/28 00:50▽光の三原色と色の三原色って言い方をするけど、よく考えるとおかしいな。だって光の三原色も色のことを言ってるわけで

10/28 00:52▽数学をあきらめた人がよくホーテーシキとかサインコサインとか何の役にやつんだよという物言いをするが、脳トレとか百ます計算とかもっと意味がないような気がする

10/28 01:17▽ヤプーに人権なし by 沼正三 RT @mainichijpedit ネットオークション詐欺、「ヤフーに責任なし」が最高裁で確定しました http://bit.ly/2t7qiy

10/28 01:21▽空目ってそらめと読んで幻覚のことなんですね。大辞林にも載ってるー。お大事に! RT @dankogai 今日はやたらと空目が多いと思ったら、左目にものもらいが急速発達中。

10/28 01:29▽家の中で夜な夜なiPhoneのヘッドセットが増殖してる気がしてならない bon nuit monde!

10/28 01:35▽また外国でカミカゼとか言われないといいが

10/28 02:23▽さっきからなんか軋るような音がする。サーバーもプリンターも暖房も冷蔵庫も停めて見たが音は止まらない。外からかと思うが外に出ると普通の雑音に紛れて聞こえない。気にしない事にして寝る。とか言って実は何らかの怪獣的な物の音でこのtwitが俺の遺言になったら超ウケるo(^▽^)o

10/28 02:32▽「たとえば140文字という制限がある中で、もう140文字、広告を足してもいいのかという点についてはやや懐疑的」とはヤフー社長の弁。わしゃややどころか大いに疑問じゃ

10/28 02:36▽「雑音とは我々が無視する事を学んだ音」って誰の言葉だっけ

10/28 21:40▽ヨドバシにて DVDとブルーレイを見比べてくださいとあるので美女が入浴している大画面を注視していると、美女が自分の口に手を突っ込んで顔を裂き始めた

10/28 21:45▽ヨドバシにて 87K円のテレビが欲しかったのだがビックカメラの価格を検索すると81K円だったので店員にその画面を見せるとビックカメラ価格になった 便利な時代だ

10/28 22:19▽ヨドバシにて「エコポイント申請のために保証書のコピーと領収書の原本をご郵送ください。その際保証書に店頭印がある事とありますがなくても大丈夫です。なお保証書の原本は店頭印の代わりとして領収書のコピーを合わせて保管してください」なんだそれ

10/29 00:32▽国というのは戦争ゲームのチームで、戦争がなければ国は必要ない、という主張が丸谷才一の「草まくら」にあった RT @dankogai: わしゃ嫌国者だよ。自国が嫌いという意味ではなく、国というシステムが嫌いという意味で @FTTH 愛国愛国って云うけど嫌国者って見たことねーよ

10/29 00:50▽「川を渡れ!」「川を渡れ!」って曲、「お前らモーヲタなんかやめて、とっととAKBヲタになっちまえ!」というメッセージなんだろうか RT @BUBKA_henshuubu: AKBINGO!見ると風邪が治る(気がする(DPRK

10/29 01:09▽「うちはテレビやビデオだけでなくドアホンもカメラもビエラリンクでコントロールしてる。そのうち人間もコントロールできるのではないか(笑)」とは「家電芸人」品川の弁。たしかにあなたはもうパナソにコントロールされている

10/29 01:33▽PayPalって考えたらすごい名前「支払い友達」遠距離援助交際みたいな

10/29 01:41▽相変わらず家鳴り 原因不明 風に連動してる気がする 台風で何かが外れたとかかなぁ 外に出ると全然音がしないし深夜にならないと他の音に紛れて聞こえないのがタチ悪い こういう時どうすればいいのかな とりあえずiPhoneで録音してみるか

10/29 01:53▽@dankogai なるほど友達に支払えという命令文ですね。いちおうペンパルに掛かっていることに気付いたことから生じた疑問というかおかしみでした。

10/29 01:55▽うまく録音できん。もう音楽掛けてねるなう。何日かしたら音がしなくなるかうまい対処が思いつくであろう。広瀬正のツィスを思い出す

10/29 02:16▽iPhoneよりiPod classicの方がミュージックプレイヤーのUIはどうしようもなく使いやすいなぁー。iPod classic風のiPhone用スキン作って欲しいりんご

10/29 08:36▽@tokuriki はじめまして。非常勤のセミナー講師してました。参加者の中には、会社から言われてラクな仕事として座っている輩が少なからずいるようで、彼らは質問とかして無駄な仕事を増やそうとしません。私はこっちからドンドン質問して彼らの仕事をラクにしないように努めてました。

10/29 17:48▽結婚詐欺と言えば映画「クヒオ大佐」公開 タイミング良いのか悪いのか

10/29 17:56▽あやや版スケバン刑事の前夜の新宿にも大勢HLが動員されていた。HLヲタHLヲタHLHLヲタヲタみたいな。どっちともつかない人もいた。混成で盛り上がってる人たちもいた。RT @mainichijpedit 路上生活者を使ってダフ屋行為 http://bit.ly/2qJH4b

10/29 18:03▽やせててもメタボ。一本でもにんじん。愛しても人の妻。分け入っても分け入っても青い山 RT @asahi やせてても「メタボ」 国際組織が新たな基準 asahi.com 話題

10/29 23:29▽スタン・ハンセンが宣伝すればいいのに「ウィー」(あれはWinと言ってるらしいが)RT @mainichijpedit 「Wiiは失速した」… http://bit.ly/2iAIbU さすがの任天堂もちょっと厳しいようです。

10/30 00:34▽通販でハードディスクを買ったら保証書がボール紙箱の内蓋に印刷してあってたまげた。例によって保証書印はないから納品書を一緒に保存しろだと。ナメられてるわあ http://twitpic.com/ne5df

10/30 00:54▽さいきん睡眠が足りてない。もう寝ます。そう言ったら明日起きるまでもうつぶやかん

10/30 20:07▽うわマジかよー 自分の中で毀誉褒貶あるけど亡くなると淋しい 星になった星の王子様 安らかにRT @mainichijpedit 落語家の三遊亭円楽さんが亡くなりました http://bit.ly/2Hpd7W

10/30 22:38▽ここ2日程、家鳴りだとか怪獣の蠢く音だとか言ってた異音の正体判明。暖房を切るために導入したanx CT12というコンセントタイマーの動作音。しょうもなくてゴメン。ああスッキリ

10/30 23:55▽ああ異音がしない家っていいもんだなあ(理想低すぎ)窓から月が見える。 http://twitpic.com/nirbk

10/31 18:54▽精神疾患を例に挙げれば同意してくれると思うけど、新しい病名をつけることで、その症状が増えるという現象があります。ー「死刑のある国ニッポン」より

11/02 19:32▽日本でインドネシア人の看護士を育成しているが漢字が難しいのが問題だそう。「褥瘡」「仰臥位」確かに難しい

11/03 12:47▽近所に幼女が集まって遊んでる一角があって側を通るのが楽しみなのだが、なんか幼女の数が日に日に増えてるような気がする

11/03 14:29▽「まずい、もう一杯!」というCMに「※個人の感想です」というテロップが付いたf^_^;)

11/04 11:15▽前から思ってたけどこの人とジーパンて関係ないよね?RT @mainichijpedit 「構造主義の巨人」レヴィ=ストロース氏が亡くなりました http://bit.ly/2CkOta 著作を再読したくなりました

11/04 11:17▽唐津君の家で何があったのかと思ったRT @asahi 唐津くんち「お旅所神幸」 14台の曳山、豪勢に asahi.com 話題 http://www.asahi.com/national/update/1103/SEB200911030012.html

11/04 11:20▽これ初期型廉価版持ってたけどあまりにも深く眠ってて可哀相に思うのか鳴らない時があったRT @tokuriki 今話題の記事: 浅い眠りの瞬間を狙って起こしてくれる「スリープトラッカー」 http://bit.ly/2rIoQ0 (via @amntwit)

11/04 15:31▽開港の開って字を何に間違えたんだろ http://twitpic.com/o7vgn

11/04 15:33▽何を予定してるんだか http://twitpic.com/o7vlo

11/05 01:43▽日シリって◯●◯●◯●◯ってパターンがまだ一回もないそうだが今回はどうかRT @mainichijpedit 【日本シリーズ】日本ハムが逃げ切り http://bit.ly/z3U9x 主砲の高橋選手の活躍が目立ちましたね

11/05 02:06▽ヨドにスイカが入るiPhoneケースというのがあって丸ごと? なぜ入れる? とびびった

11/05 14:37▽あははは、そうか、こういう風に間違えたんだ。これは恥ずかしい。 http://twitpic.com/obxzl

11/06 12:54▽「釣りバカ日誌の浜ちゃんは、釣りを仕事にすべき」と立ち読みした本に書いてた フーン

11/06 12:57▽大人の川崎のぼる~♪

11/06 13:00▽自衛隊の受付かと思った http://twitpic.com/ofx9i

11/06 13:25▽オイラは左官屋さんかと思ってた(^○^)RT @mainichijpedit ベルリンの壁って誰が作ったか知ってます? http://bit.ly/3dOOr3 ロシア人の過半数は知らないそうです

11/06 14:09▽このニュース、「女が」「女の知人が」と言ってて平安文学かよ!と思うRT @mainichijpedit おはようございます、コッコです。鳥取不審死、こちらも知人が次々と… http://bit.ly/pufZz

11/06 17:48▽菅さんだけにセフレ退治策かと思った(忘れてやれよ!)RT @mainichijpedit 勝間和代さんが、菅直人副総理にデフレ退治策を提言。

11/06 17:57▽はじめまして。ぼくも早朝執筆するぞの会に入れてください!(勝手に会にしてごめんなさい!)年明けに締め切りあるのに全然進んでないんです!(^_^;RT @kuwako 4時おき、5時から6時に本書きを決めています(^ ^)

11/06 20:51▽郵便局にて 確かにペットボトルも空き缶も燃えるのに「燃えるゴミ」っておかしいと思ってた http://twitpic.com/ogv0y

11/06 20:53▽郵便局にて さっきからSECOMの人がATMにお金を入れようとして手間取ってる 「ちくしょう!」とか言っててビビった(^_^;

11/06 20:55▽SECOMとALSOKって徒手空拳で戦ったらどっちが強いのかな

11/06 21:37▽同意ありがとうございます。そうと決めたらぼくは今日12時に寝て7時に起きます~(^_^ RT @kuwako 早朝執筆会ですか笑。そういう仲間を集めて励まし合いながら書いて行きたいですね。よい考えだと思います。

11/07 07:09▽おはようございますっておくれました。。。早速執筆~RT @kuwako 今日はドップラーが終わりました!あと少し!7時から頑張ってください!

11/07 12:28▽use utf8; binmode(STDOUT, ':utf8'); no utf8; {use bytes; use Encode; print decode('UTF-8', substr('日本語', 0, 3));} で日と表示もno utf8;を取るとWideと怒られ

11/07 19:32▽ストパー(ストレートパーマじゃなくてStrawberry Perl)にEncode::JIS2Kを入れようとしたらenc2xsがないと言われてMakefile.PLが止まった

11/07 19:39▽若くて美人のFPが「老後に十分な資金を貯めるには」とか言ってもあんたまだ老後迎えてないじゃないのと思うけど、多くの(幸福|悲惨)な老後を見て研究した結果ということだからいいのかしら

11/07 23:12▽今日、頁数は進まなかった(構想してただけ)けど中身は濃かったです。やっぱ朝方はいいですね! 明日も7時起きで行きます~ @kuwakoさん、ぼく科学も好きですので御本読みます! RT @kuwako: 今日はドップラーが終わりました!あと少し!7時から頑張ってください

11/07 23:21▽@OkawauchiMari ぼくも「早朝執筆の会」です!(^o^) 一緒にがんばりましょう~

11/08 00:20▽キムラタクヤ引退。。。。。。(巨人の野球選手の拓也の方じゃが。。。。。。

11/08 00:22▽日シリ巨人連勝で結局◯●◯●◯●◯ならず。実はものすごく難しいことなのか

11/08 07:00▽おはようございます 6:55 に目覚めて7時セットの目覚ましを止めました (^_^ コンビニで朝食買って散歩してきまーす♪RT @OkawauchiMari おぉぉっ! いっしょにがんばってる方がいてくださると、大変励みになります! がんばりましょうね

11/08 07:23▽歩道に子供の靴下が片方落ちてた 子供片足寒くないかな http://twitpic.com/oo6cv

11/08 07:33▽AKB48のRIVERという曲を聞いてテンション上げてたら多摩川に出た(^_^ http://twitpic.com/oo83z

11/08 08:01▽帰宅~ こっちも執筆開始します~ @kuwako @OkawachiMari

11/08 08:18▽みなさんがんばってますね。ぼくも早く壁にぶつかるところまで行きます (^^ @OkawauchiMari @kuwako @okawauchimari @sanshirou346 @denim_road

11/08 09:08▽パソコンを使ってると時々異音がするのが気になってたのだが「Windowsの検索のアニメの犬がヒマで爪を研ぐ音」だった 余計なことせんでええ

11/08 11:08▽あまりにも血圧が変わらないので血圧計を大家さんに譲った 高級な野菜ジュースをもらった(^^)

11/08 12:46▽昔「じみへん」というマンガで、人間の体がどこまで落ちてたら殺人事件と見なしていいかというネタがあったな~

11/08 14:12▽人間魚雷回天

11/08 14:47▽博多水炊き風ラーメンって出来ないかな~と思いながら歩いてたらそういう店があってびっくり http://twitpic.com/oq59d

11/08 14:50▽休日昼寝しちゃって夜眠れなくなるのが悩みだったんだけど、昼眠くなったら散歩すればいいな。オーディオブックも聞けるし

11/08 15:25▽ナニコノ車止め可愛いけどカエル?サル? http://twitpic.com/oq9ll

11/08 17:59▽そうそう。ウィンクしてるのとそうじゃないのと大量にいる (^^) RT @igarikun: 日吉東急の近く? RT @query1000 ナニコノ車止め可愛いけどカエル?サル? http://twitpic.com/oq9ll



http://twitter.com/query1000

| ホーム |


 ホーム 


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。