LoginSignup
24
19

More than 5 years have passed since last update.

コード中にUnicode文字列を表現する

Posted at

ひとつきほど前に書き殴ったけど放置されてたので放流する。

前提

  • Unicodeは文字集合であり、文字ごとにコードポイントが割り当てられる
  • UTF-8はUnicodeをバイト列として表現するための方式(文字符号化方式 - Wikipedia)のひとつである
  • UTF-8は文字によってバイト長が異なる
  • PHPはASCII互換であれば、エンコーディングに依存しない言語である
    • PHPの「文字列」は単なるバイト列である
  • もちろんPHPの標準ライブラリはマルチバイト文字列処理をサポートする
  • 言語コアがエンコーディングをサポートするRubyではソースファイルに不正なバイト列が含まれるとSyntaxErrorだが、PHP5では(Syntaxと矛盾しない限り)そのようなことはない
  • 現在のPHPでの標準的なエンコーディングはUTF-8である

各手法の比較

文字列リテラル

<?php
$wave_dash        = '〜';
$full_width_tilde = '~';
  • pros
    • 実行時にコストがかからない
      • コード上にバイト列が直接埋め込まれてるので
    • クラス定数(const)に定義することが可能
  • cons
    • 近年の多くのフォントではグリフで判別がつかない※ただしWindozeを除く
    • Unicodeを正しく扱ってくれないテキストエディタなどで無惨に破潰される
      • TEditorコンポーネントベースのエディタなど
      • TeraPad, MKEditorなど古来から日本人に愛用されてきたエディタが該当するんじゃよ…

JSON

Introducing JSONを読め。

JSONでは\uとUnicodeのコードポイント16進数4桁でユニコード文字を表現することができるので、これを利用する。

<?php
$wave_dash        = json_decode('"\u301C"');
$full_width_tilde = json_decode('"\uFF5E"');
  • pros
    • Unicodeを正しく解釈できないエディタ/処理系に殺されることはない
    • メソッド呼び出し一回で済む
  • cons
    • クラス定数(const)に定義することができない
    • '"xxx"'みたいに二重括弧で括るのがわかりにくいし、コンパイラレベルのエラーにならない
    • 文字列を取得したいためにjson_decodeって書いてるのが直感的ではなくバッドノウハウくささがある

chr-implode

<?php
$wave_dash       = implode('', array_map('chr', [0357, 0275, 0236]));
$full_width_dash = implode('', array_map('chr', [0343, 0200, 0234]));
  • pros
    • Unicodeを正しく解釈できないエディタ/処理系に殺されることはない
    • PHPの文字列が単なるバイナリ列だと理解してると直感的
    • JSONモジュールが無効な環境でも動作する
  • cons
    • クラス定数(const)に定義することができない
    • 一文が長く、周りくどく見える

まとめ

  • なんかこのへんの知見が共有されてないんじゃないかと思ったので書いた
  • ぶっちゃけどれでもいい
  • ただし、
    • 筆者はconstに定義したいのでリテラルで書くことにした
    • 数も多くないのでPHPUnitでchr-implodeを使って作った文字列と比較して、不埒者にリテラルを破潰されるリスクを未然に防ぐことにした
  • 個人的にはJSON形式で書くとUnicode文字列、chr-implodeで書くとUnicode文字であることを強調できる気がする(気分の問題)

あとがき

TEditorの功罪について誰か語ってください。人類がJISコードの重力井戸の底に居た頃はこの上なく素晴らしかったのですが…

2014年の現在において、Unicode/UTF-8対応を謳っておきながら、JIS X 0208範囲外の文字を認識できないエディタの存在は邪悪だと、はっきり申し上げたい。


おまけ バイト列の数値表現を調べる

本文中ではUnicodeの16進表記とUTF-8の8進表記を使った。

array_map("ord", str_split("〜")));
//=> array (
//       0 => 227,
//       1 => 128,
//       2 => 156,
//   )

array_map(function($n){ return "0".decoct(ord($n)); }, str_split("〜"));
//=> array (
//       0 => '0343',
//       1 => '0200',
//       2 => '0234',
//   )
)

json_encode("〜", ~JSON_UNESCAPED_UNICODE)
#=> '"\\u301c"'
24
19
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
19