Unicodeテキストの処理には2つの段階があります。1つ目は、「情報を失うことなく、どのように入力および出力できるか」です。2つ目は、「現地の言語規則に従ってテキストをどのように扱うか」です。
tchristの投稿は両方をカバーしていますが、2番目の部分は彼の投稿のテキストの99%が由来する場所です。ほとんどのプログラムはI / Oも正しく処理しないので、正規化と照合について心配する前に、それを理解することが重要です。
この投稿はその最初の問題を解決することを目的としています
データをPerlに読み込むとき、それがどのエンコーディングであるかは関係ありません。それはいくつかのメモリを割り当て、そこにバイトを隠します。あなたが言うならprint $str
、それはあなたの端末にそれらのバイトを送信するだけです、それはおそらくそれに書き込まれるすべてがUTF-8であると仮定するように設定されており、あなたのテキストが表示されます。
素晴らしい。
ただし、そうではありません。データをテキストとして処理しようとすると、何か問題が発生していることがわかります。あなたはそれ以上行く必要はありませんlength
Perlがあなたの文字列について考えていることと、あなたの文字列についてどう思っているかが一致していを確認ます。次のようなワンライナーを書いてperl -E 'while(<>){ chomp; say length }'
、タイプしてください文字化け
します。と、12が返されます...正解ではありません。4。
これは、Perlが文字列をテキストではないと想定しているためです。それがあなたに正しい答えを与える前にそれがテキストであることをあなたはそれに言わなければなりません。
とても簡単です。Encodeモジュールには、そのための関数があります。一般的なエントリポイントはEncode::decode
(またはuse Encode qw(decode)
、もちろん)です。この関数は、外界からいくつかの文字列(「オクテット」と呼びます。「8ビットバイト」と言います)を受け取り、Perlが理解できるテキストに変換します。最初の引数は、「UTF-8」、「ASCII」、「EUC-JP」などの文字エンコーディング名です。2番目の引数は文字列です。戻り値は、テキストを含むPerlスカラーです。
(もあります Encode::decode_utf8
エンコーディングにはUTF-8を想定してます。)
ワンライナーを書き直すと:
perl -MEncode=decode -E 'while(<>){ chomp; say length decode("UTF-8", $_) }'
文字化けと入力すると、結果として「4」が得られます。成功。
これが、PerlのUnicode問題の99%に対する解決策です。
重要なのは、プログラムにテキストが入力されるたびに、それをデコードする必要があることです。インターネットは文字を送信できません。ファイルは文字を格納できません。データベースに文字がありません。オクテットしかなく、Perlではオクテットを文字として扱うことはできません。Encodeモジュールを使用して、エンコードされたオクテットをPerl文字にデコードする必要があります。
問題の残りの半分は、プログラムからデータを取得することです。それは簡単です。あなたが言うだけでuse Encode qw(encode)
、あなたのデータがどのエンコーディングになるかを決定し(UTF-8はUTF-8を理解するターミナル、Windows上のファイルのUTF-16など)、encode($encoding, $data)
単に出力する代わりに結果を出力し$data
ます。
この操作は、プログラムが操作するPerlの文字を、外部の世界で使用できるオクテットに変換します。インターネットまたはターミナルに文字を送信できればはるかに簡単ですが、オクテットのみは送信できません。したがって、文字をオクテットに変換する必要があります。変換しない場合、結果は未定義です。
要約すると、すべての出力をエンコードし、すべての入力をデコードします。
次に、これを少し難しいものにする3つの問題について説明します。最初はライブラリです。彼らはテキストを正しく処理していますか?答えは...彼らは試みます。Webページをダウンロードすると、LWPは結果をテキストとして返します。結果に対して適切なメソッドを呼び出すと、それが起こります(たまたま、サーバーから取得したオクテットストリームにすぎdecoded_content
ませんcontent
)。データベースドライバーは不安定な場合があります。PerlだけでDBD :: SQLiteを使用した場合、問題は解決しますが、他のツールがデータベースにUTF-8以外のエンコーディングとして格納されたテキストを配置した場合...まあ...それは正しく処理されません正しく処理するコードを書くまで。
通常、データの出力はより簡単ですが、「印刷されたワイド文字」が表示された場合は、どこかでエンコーディングがめちゃくちゃになっていることがわかります。その警告は「Perlの文字を外部に漏らそうとしているので、意味がありません」という意味です。プログラムは動作しているように見えます(通常、もう一方の端が生のPerl文字を正しく処理するため)。プログラムは非常に壊れており、いつでも動作を停止する可能性があります。明示的に修正してくださいEncode::encode
!
2番目の問題は、UTF-8でエンコードされたソースコードです。use utf8
各ファイルの冒頭で述べない限り、PerlはソースコードがUTF-8であると想定しません。これは、のようなことを言うたびmy $var = 'ほげ'
に、プログラムにゴミを注入し、すべてを恐ろしく破壊することを意味します。「utf8を使用する」必要はありませんが、使用しない場合は、プログラムで非ASCII文字を使用しないでください。
3番目の問題は、Perlが過去をどのように処理するかです。昔、Unicodeのようなものは存在せず、PerlはすべてがLatin-1テキストまたはバイナリであると想定していました。そのため、データがプログラムに入ってきてテキストとして扱い始めると、Perlは各オクテットをLatin-1文字として扱います。そのため、「文字化け」の長さを尋ねると、12が得られました。Perlは、Latin-1文字列「æååã」(12文字で、一部は非表示)で動作していると想定しました。
これは「暗黙のアップグレード」と呼ばれ、完全に合理的なことですが、テキストがLatin-1でない場合、これは望ましくありません。そのため、入力を明示的にデコードすることが重要です。それを行わないと、Perlがそうし、間違って行う可能性があります。
データの半分が適切な文字列であり、一部はまだバイナリであるという問題が発生します。Perlは、まだバイナリである部分をLatin-1テキストであると解釈し、正しい文字データと結合します。これにより、キャラクターを正しく処理することでプログラムが壊れたように見えますが、実際には十分に修正していません。
次に例を示します。UTF-8でエンコードされたテキストファイルを読み取るプログラムがあり、PILE OF POO
各行にUnicode を付加して印刷します。あなたはそれを次のように書きます:
while(<>){
chomp;
say "$_ 💩";
}
そして、次のようないくつかのUTF-8エンコードされたデータで実行します。
perl poo.pl input-data.txt
各行の終わりにpooを付けてUTF-8データを出力します。完璧です、私のプログラムはうまくいきます!
しかし、いいえ、あなたはバイナリ連結をしているだけです。ファイルからオクテットを読み取り、\n
with chompを削除してから、PILE OF POO
文字のUTF-8表現のバイトを追加します。ファイルからデータをデコードして出力をエンコードするようにプログラムを修正すると、pooの代わりにガベージ( "ð©")が発生することに気付くでしょう。これにより、入力ファイルをデコードするのは間違っていると信じるようになります。そうではありません。
問題は、pooがlatin-1として暗黙的にアップグレードされていることです。あなたがいる場合use utf8
、バイナリの代わりにリテラルテキストを作るために、それが再び動作します!
(これが、私がUnicodeを手助けするときに私が目にする最大の問題です。彼らは正しく機能し、プログラムを壊しました。それは、未定義の結果の悲しいことです:長い間、正常に機能するプログラムを持つことができますが、それを修復し始めると、心配する必要はありません。プログラムにエンコード/デコードステートメントを追加しているときに問題が発生した場合、それは単に実行する作業が増えることを意味します。次回、最初からUnicodeを念頭に置いて設計すると、はるかに簡単です!)
これが、PerlとUnicodeについて知っておくべきことのすべてです。Perlにデータが何であるかを伝えると、すべての一般的なプログラミング言語の中で最も優れたUnicodeサポートを備えています。ただし、どのような種類のテキストを供給しているのかを魔法のように知っていると仮定すると、データを取り返しのつかない形で破棄することになります。プログラムがUTF-8端末で今日機能するからといって、明日、UTF-16でエンコードされたファイルで機能するわけではありません。だから今は安全にして、ユーザーのデータを破壊する頭痛を取り除いてください!
Unicodeの処理の簡単な部分は、出力のエンコードと入力のデコードです。難しいのは、すべての入力と出力を見つけ、それがどのエンコーディングであるかを判断することです。しかし、それがあなたが大金を得る理由です:)