UTF-8について一言

Perlは長い間エンコーディングについて何も知りませんでした。 文字列はバイトのシーケンスであり、誰もが必要なものをすべてそこに保持し、このデータがどのようなエンコードであるかを時折考えなければなりませんでした。 時代は変わり、UTFが登場しました。 Perlistsも彼をサポートしなければなりませんでした。 通常のように、perlの方法で。 この記事が、PerlでのUTF-8の実装をまだ知らない人たちの健康を少しでも救うことを願っています。



実際、PerlにはUTF-8の実装が2つありました。 最初のものはPerl 5.6で登場しましたが、かなり粗野で不便でした。 Perl 5.8以降、Unicodeメカニズムは根本的に改訂され、CPANのモジュールには、インタープリターのバージョンに関する面白いチェックがいっぱいです。 以下に記述されているものはすべて、正確にこの2番目の実装に関連しています。



長所と短所



まだエンコーディング、冷静に開発された単一言語アプリケーションについて考えていなくて、同じやり方で続けるつもりなら、ほぼ確実にユニコードは必要ありません。 いずれの場合も、シングルバイトエンコーディングのデータはよりコンパクトであり、より高速に処理され、簡単かつ快適に処理できます。



データの次の部分がアプリケーションに送信される形式がわからない場合、または国際プロジェクトを開発している場合は、おそらくUTF-8が必要になります。 実際、あなたのサイトが英語であっても、名前にウムラウトが付いているドイツ人、または中王国の居住者でさえ登録することができます。 データベースでその後何が起こるかを考えない最も簡単な方法(お気に入りのlatin-1で中国語の名前を表示する方法について)は、多くの言語をサポートするエンコーディングで作業することです。



また、Perl UTFを知ることができないもう1つのケースは、この形式で動作するサードパーティコンポーネントとの統合です。 たとえば、 XML::LibXML



は、この形式のXMLファイルの解析結果を返します。



perlの方法



おそらく、鉱夫たちは次のようなことを主張しました。変数にバイトチェーンを格納したので、そこに文字を格納する方法を学ぶ必要があります。 UTF-8の文字長は一貫性がなく、1バイト以上になる場合があります。 文字列を操作するためのレギュラーと関数( length



substr



)の動作が異なる場合、彼らは感謝を言いません。
したがって、2つのタイプの文字列を作成する必要があります-古いパターンで動作するためのbytesと、新しいパターンで動作するためのcharactersです。 どうやってやるの? そして、スカラーに隠しフラグを導入しましょう。 フラグが設定されている場合、文字列は論理文字( Perl内部形式と呼びましょう)で構成されていると認識されます。そうでない場合は、バイトから認識されます。



2つの同一のUnicode変数を使用して、そのうちの1つのフラグをドロップするだけの場合、変数は真珠によって異なる方法で処理されます(たとえば、長さが異なる可能性が高い)。 ただし、データ自体は同時に変更されません-これは、たとえば、両方の変数がファイルまたは画面に表示されている場合に確認できます。



Perlの用語ではUTF-8文字はしばしばワイド文字と呼ばれることに言及する価値があります。 これらの単語で警告に出くわすと、ユニコード文字列になります。



PerlでUnicodeデータを操作するためのオプションがいくつかあります。 主なものは次のとおりです。

  1. 文字列内のユニコード文字の強制的な表示- \x{0100}



    形式の構築。
  2. Encode



    モジュールまたはutf8



    パッケージの関数を使用した文字列の手動再utf8



  3. enable pragma use utf8



    フラグは、コード内で検出されたすべての定数に対して発生します。
  4. IO層を示すI / O記述子からの読み取り:encoding



    または:utf8



    すべてのデータは自動的に内部形式に変換されます。
第1段落では、すべてが明確であり、彼が質問をしないことを願っています。 念のため、中括弧が必要であることに言及します。 残りのオプションについてさらに詳しく検討します。



Encode



モジュール


このモジュールはPerl 5.8に含まれているため、Unicodeだけでなく他のエンコード変換にも使用するのが理にかなっています。 モジュールの操作はそれほど複雑ではありません。 唯一の問題は、 encode



機能とdecode



機能を混同しないことを学習decode



ことです:-)。 それらは同じインターフェースを持ち、命名ロジックは私たちが望むほど明白ではありません。 Unicodeフラグのある文字列の形式は内部形式と見なされるため、任意のエンコード(フラグのないUTF-8を含む)からデータをデコードする必要があります。逆も同様です。特定の外部エンコードにデータを転送する場合は、内部形式からエンコードする必要があります彼女。 次のようになります。



$bytes = encode('cp1251', $string); # cp1251

$string = decode('cp1251', $bytes); #








あるエンコードから別のエンコードにすべての文字を失うことなく追跡できるわけではないため、問題が発生した場合の動作を決定する3番目のパラメーターもあります。 Encode



モジュールのドキュメントで
それについて読むことができます 。そこにはセクション全体がこれにEncode



られています。



変数にUTF-8のバイトが含まれていることが確実な場合は、 _utf8_on



再コーディングおよびチェックする必要なく、変数のフラグを立てることができます。 関数is_utf8



は、行にフラグがあるかどうか(およびそこにあるデータの有効性を確認するかどうか)を判断するのに役立ちます。 ご_utf8_off



、フラグは_utf8_off



によってリセットされます。 唯一の「しかし」-これらの関数はINTERNALとしてマークされており、不変性に頼るべきではありません。



Perl 5.8.1以降、 Encode



モジュールの関数の一部がutf8::



名前空間で使用可能になりました-これらはis_utf8



encode



decode



関数is_utf8



。 後者の2つは、結果を返す代わりに渡された変数の値を変更し、エンコードを必要としないという点でEncode



モジュールの同義語と異なります(フラグを立てずにUTF-8データを処理することが理解されています)。 これらの関数はすべてインタープリターに組み込まれているため、これらにアクセスuse utf8



use utf8



必要はありません-さらに、これは追加の効果をもたらす可能性があります(少し後で)。



use utf8;





use utf8



は、その操作のゾーンに書き込まれ、非ASCII文字を持つすべての定数と正規表現をUnicodeとして扱い、内部形式に自動的に変換する必要があることをインタープリターに伝えます。 プラグマをキャンセルするには、通常どおり、 no utf8



コンストラクトが使用されます。



プラグマuse bytes



するという意味では、逆もありuse bytes



。この領域では、UTF-8フラグを持つデータでさえ、バイトで構成されるものとして扱われます。



PerlIO


Perl IO Layersテーマは、原則として別の記事に値します。 アイデアは、しばらくの間、古き良きopen



関数が3つの引数の構文を取得したということです。



open $fh, $mode, $filename







$mode



のタイプ'>'



および'<'



の標準値に加えて、ファイルのエンコードを指定することもできます。 この場合、ダウンロードされたデータは自動的に内部Perl形式に変換されます。



open $fh, "<:encoding(cp1251)", $filename







UTF-8のデータを含むファイルについて話している場合は、コードを少し単純化できます。



open $fh, "<:utf8", $filename







もちろん、これらの修飾子を使用してファイルを変更できます-効果は逆になります。



ところで、Perlには、 -C



コマンドラインスイッチを使用して、I / Oストリームを一度だけUnicodeにする機能があります。 詳細は、いつものようにperldocで見ることができます。



すくい



もちろんそうです。 一般に、開発のあらゆる段階で、Perlは自身の周りにさまざまなレーキをばらまき、それをプログラマが慎重に収集するという感じがすることがあります(最初のレーキが実験的な場合は2回)。



最初に、一部の関数は、定義上、文字ではなくバイトで動作し、内部表現の行は喉を横切って上昇します。 これらの関数には、 Digest::MD5



モジュールで頻繁に使用される関数が含まれます。 そのため、指定された例はWide character in subroutine entry at test.pl line 3.



エラーWide character in subroutine entry at test.pl line 3.



落ちます。



use Digest::MD5 'md5_hex';

print md5_hex("\x{400}");








第二に、データは常にプログラムがそれを見ると期待する形式になっているわけではありません。 たとえば、有効なUTF-8が常にHTMLフォームハンドラーに来ることを期待するのは単純です。 ソースに対する過度の信頼の結果は、データの破損から始まり、別のエンコーディングへのトランスコード(たとえば、電子メールの生成時)での致命的なエラーで終わる、非常に多様なものになります。



最後に、最も一般的で興味深い問題は、2つの文字列を連結しようとしたときに発生し、そのうちの1つだけが内部の真珠の形式で保存されます。 このようなファイル(UTF-8で記録)があるとします:



use Encode;

$a = decode('utf8', " "); #

$b = " "; # 15

$c = $a.$b;








最後の行では、Perlは文字列を共通の分母形式にキャストしようとします。 彼は$b



をバイト文字列として認識しているため、この文字列の各バイトはUTF-8でエンコードされています。 結果は、この混乱のようなものです(ちなみに、フラグが立てられています):



$c = " на Хабре"







グリッチは、ユニコード固有のワニを通して肉眼ではっきりと見える-あなたはそれを何かと混同することはできません。



おわりに



この記事では多くの微妙な点が未解決のままでした。 Encode



モジュールの多くのユーティリティutf8



残っていました。 UTF-8の観点からは無効な文字に敏感な内部形式のバリエーションについて言及する場所はありませんでした。 正規表現に関連する質問は完全に省略されています。 このトピックを最後まで掘り下げたい場合は、マニュアルに注意してください。

ご質問がある場合は、お答えします。



UPD:コード署名habrayuzerは、同じトピックに関する彼自身の開発へのリンクを送信しました。



All Articles