読者です 読者をやめる 読者になる 読者になる

Perl の内部表現の一つはたまたま UTF-8 です。内部表現は何か分からないエンコーディングで、常に明示的にエンコードと デコードが必要ということにしておいた方がよいです。

追記

ajiyoshiさんの昔の記事がとてもわかりやすかったので参考に。
VOYAGE GROUP エンジニアブログ : あなたにも今日こそPerlの文字化けが理解できるたった一つの原則

元記事

記事タイトルはperlunifaqの「UTF8 フラグ」って何?からそのまま引用したもの。
昔は「UTF8フラグってよくわからない」ってなってたけど、この1文読むだけで「内部表現がたまたまUTF-8だったからUTF8フラグって言葉になったのか」とか、「そもそも内部表現が何かってことは気にする必要ないのか」って思えていい。


で、その辺の話を説明してくれている人がいたので引用。

Perl上級者コーナーPart01

503 :nobodyさん [↓] :2011/08/26(金) 02:37:40.52 ID:???
>>501
難しく考えすぎ。難しいと勘違いしているのなら、その原因はutf8(フラグ)という名称についてだけ

Perlには ”Perl文字列”(命名俺)と "バイナリ" の二つしか無い。(数値やオブジェクトはまあとりあえず忘れて)
"Perl文字列"は、その名の通りPerl言語専用の文字列。だからこの文字列をPerlの世界の外に存在しないし、外に出すことはない。
それはつまり、ネットワーク通信やファイル読み書きで外部に流してはいけないということを意味する。

"バイナリ"はただのバイトの並び。ネットワークやファイルから読み取るものは全部"バイナリ"(PerlIOという自動変換機能を使わない限り)
テキストファイルだろうがなんだろうが、それはPerlから見ればただのバイトの並びでしか無い。

Perlにおいて文字列を扱う関数や正規表現は すべて"Perl文字列"を引数に取る。
"バイナリ"も引数にとれるがそれは古いPerlとの互換性用の機能と考える。

じゃあその"Perl文字列"はどうやって作るかというとそれがEncodeモジュール。
ネットワークやファイルから与えられた"バイナリ"をEncodeモジュールを使って"Perl文字列"に変換する。

(utf8::*もあるが忘れていい。utf8.podにはことごとくEncodeモジュールを
使ってくださいと書いてある http://perldoc.jp/docs/perl/5.8.1/utf8.pod)

ちなみにソースコードに書いてある文字列。これもデフォルトでは "バイナリ" 。
互換性のことを考えると理由はわかると思う。use utf8;をするとソースコードの文字は"Perl文字列"になる。

文字コードはたくさんあるが、"Perl文字列"は一種類しかない。Encodeモジュールを使って
いろんな"バイナリ"を"Perl文字列"に変換できる。もちろんその逆も出来る。

US-ASCIIだろうが、Latin-1 だろうが、UTF-8だろうが、Shift-JISだろうが、EUC-JPだろうが
外部にあるものはみんな"バイナリ"。それをEncodeモジュールは"Perl文字列"に変換してくれる
504+1 :nobodyさん [↓] :2011/08/26(金) 02:38:03.50 ID:???
"Perl文字列"の内部の実装がUTF8だとしても、そのことを意識する必要はない。
UTF8フラグという名前が良くないが、これはPerl文字列フラグと読み替えればいい。
Perl文字列フラグがついていればPerl文字列(繰り返すが中身がUTF8なのは知らなくていい)

http://perldoc.jp/docs/perl/5.10.0/perlunifaq.pod
> 内部表現は何か分からないエンコーディングで、常に明示的にエンコードと
> デコードが必要ということにしておいた方がよいです。


だからPerl文字列フラグ付きのUTF8やLatin-1などというものはない。
Perl文字列フラグがついていれば、それはすべて同じPerl文字列。


あとは自分のプログラムは文字列を扱うとき、"Perl文字列"を基本として使うのか(統一された文字のモダンなやり方)
"バイナリ"を基本として使うのか(互換性重視の古いやり方)を決める。"Perl文字列"なら文字と1文字として扱える。
"バイナリ"だとそうはいかない。

Perl文字列を基本として使うなら、Perl外部のネットワーク、ファイルの読み書き時にすぐに変換して
Perl文字列メインでコードを書くことになる。

もし古いモジュールがPerl文字列対応になっていなければ、バイナリに変換して呼び出すラッパーを書くことになるだろう。


反対にバイナリを基本として使うのなら、バイト数ではなく文字数を知りたい時など、
文字として扱いたいところでPerl文字列に変換して関数を呼び出す。
そういうラッパーを書くことになるだろう。


どちらを基本にするかをしっかりと自覚しているのなら、あとはラッパーが変換し、関数を呼び出し、ラッパーがもとに戻す。
ラッパーが違いを吸収して、Perl文字列だろうがバイナリだろうが、自分で決めた基本に統一できるのだから状態を確認する手間はない。
505 :nobodyさん [↓] :2011/08/26(金) 02:38:14.84 ID:???
君が望んでいる通り、いろんな組み合わせや状態なんかを考えたくないだろう?
多言語対応で作るとなったとき、プログラムで使う文字はひとつに統一したほうがいいだろう?

それがPerlでは、"Perl文字列"になる。本当はPerl外も巻き込んで一つに統一されたほうが
いいんだけど、統一という言葉に一番近いUnicodeでさえ、文字集合は同じでも、文字を表すビット列には
UTF8、UTF16、UTF32などがある。近いうちに一つに統一されることは無いだろう。

だからPerl外部からPerl内部に渡って来る時の変換機能は絶対になくせない。
(もちろんこのアプリはこの文字コードだけを使いますと決めれば変換機能を
なくせはするだろうけど、それはPerlという言語の開発者が決めることではない)

古い互換性を保つコード・機能を忘れれば、Perl文字列はPerl文字列という物一つに統一されている。実にシンプル
逆にPerlの外ではいろんな文字コードが存在し変換の必要があるが、それはPerlのせいではない。

NKFやJcodeといったPerl文字列が存在しなかった時代に作られた「"バイナリ"を無理やり
文字列とみなして操作する」古いモジュールのことを忘れればこんなにシンプルなんだ。


長々と書いたが、結論はこれだけ。
Perl外部に存在する"バイナリ" と Perl内部に存在する"Perl文字列"
"バイナリ"を○○コードとみなして、"Perl文字列"に変換するのがEncodeモジュール
506 :nobodyさん [↓] :2011/08/26(金) 02:40:07.58 ID:???
おまけ >>501の図の修正版

US-ASCII
Latin-1
UTF-8       いくつものエンコードがある
Shift-JIS     混沌とした世界
EUC-JP
その他

↑Perl外の世界の文字 (UTF-8フラグ 全部OFF)

----------------------Encodeモジュール等で変換

↓Perl内の世界の文字 (UTF-8フラグ ON)

Perl文字列 (ないしょだけど実装はUTF-8フラグ付きのUTF-8エンコード)

           たった一つのシンプルな世界


 Encodeモジュールで変換しなかった文字はどうなるかって?
 それは文字ではない。ただのバイナリ、バイトの配列だ。