#はじめに
※投稿後、いくつかコメント頂きました。主に、@mpyw さん、@hrdayaさんのコメントを元に、大幅に変更を加えています。コメントありがとうございました。
機種依存文字をスペースに置き換えたいと思い見つけたページを試したのですが、うまくいかなかったので、その時に調べた事を書いておきます。なお、特に断りのない限り、文字コードはUTF8を想定しています。
#参考にしたソース
参考ページには次のように書いてありました。
function replaceText($str){
$arr = array(
... (省略) ...
'\xE2\x91\xA0' => '(1)',
'\xE2\x91\xA1' => '(2)',
'\xE2\x91\xA2' => '(3)',
'\xE2\x91\xA3' => '(4)',
... (省略) ...
);
return str_replace(array_keys($arr),array_values($arr), $str);
}
これのやりたい事は、次のソースと等価です(環境依存なもので表示されなかったらすいません、 Windowsで入力すると環境依存と書いてあるので、正しく表示されない事もあるかもしれません。丸で囲まれた数字です。この記事では環境依存文字として扱います)。
function replaceText($str){
$arr = array(
... (省略) ...
'①' => '(1)',
'②' => '(2)',
'③' => '(3)',
'④' => '(4)',
... (省略) ...
);
return str_replace(array_keys($arr),array_values($arr), $str);
}
このように環境依存文字をソースコードに書くと、後で別の環境で見た時に意味不明になる可能性があるので避けたいです。なので、上の例のような形で置き換えをするのがベターだと思います。なお、今回の記事を書くキッカケになったのは、'😀'の時がFirefoxでは表示され、Chromeでは文字化けする事でした。これは恐らくフォントの問題だと思います。
#修正版
そもそも、大半の言語では16進数の表現は大抵0xFFのような形なので、なんでこんな事をやってるのだろう?と思い、よくよくマニュアルを見ると、\x**は正規表現の16進数表現でした。従って次のように修正しました。
function replaceText($str){
$arr = array(
... (省略) ...
// 0x2460 - 0x246F
'/\xE2\x91\xA0/' => '(1)',
'/\xE2\x91\xA1/' => '(2)',
'/\xE2\x91\xA2/' => '(3)',
'/\xE2\x91\xA3/' => '(4)',
... (省略) ...
);
return preg_replace(array_keys($arr),array_values($arr),$str);
}
これで置き換えが上手くいきました (^^)v (機種依存文字をさりげなく避けています)。なお、初めはmb_regex_encodingなども必要かと思っていましたが、@mpywさんの指摘により不要という事が分かりました。詳しくはコメントを見て下さい。
また、@mpywさんの指摘により、次のようにダブルクォーテーションで囲めば、正規表現でなくても置き換え出来ることが分かりました。これもコメントを見ると、もう少しシンプルなプログラムが紹介されています。
function replaceText($str){
$arr = array(
... (省略) ...
"\xE2\x91\xA0" => '(1)',
"\xE2\x91\xA1" => '(2)',
"\xE2\x91\xA2" => '(3)',
"\xE2\x91\xA3" => '(4)',
... (省略) ...
);
return str_replace(array_keys($arr),array_values($arr), $str);
}
でもプログラムのコメントにあるように、UTFでは①(丸1)は0x2460で表現されます。では何で、'\xE2\x91\xA0/'でうまくいくのでしょう?
#文字列の内部表現
で、ここで試してみたのがbin2hex
です。
echo bin2hex('①'); //結果は0xE291A0
プログラムの正規表現とマッチします。なお、今回は参考ソースに置き換えしたい文字が無かったので、bin2hex
でを使って置き換えの正規表現を書きました。
#UTF8について
ここでは、指摘されたコメントを元に調べた事について書きます。もし置き換えだけ出来ればいーやという方は無視して下さい。
別のセクションで次のように書きました。
UTFでは①(丸1)は0x2460で表現されます。では何で、'\xE2\x91\xA0/'でうまくいくのでしょう?
これは少し間違っています。0x2460というのは、Unicodeで定義されている文字コードです。これを、UTF-8の方式で変換したものが、0xE291A0になります。この辺りの定義の理解があいまいだったのですが、Unicodeは文字集合とそれに対応する文字コードで、UTF-8はその文字コードを符号化する方式す。変換の方法などは、UTF8のWikipediaか、本当は怖い文字コードの話が分かり易かったです。
#bin2hexについて
このセクションは完全に蛇足なのですが、過去に書いた勘違いを残しておこうと思い、書いておきます。
bin2hexのマニュアルを見ると次のように書いてあります。
str を16進表現に変換したASCII文字列を返します。変換は、上位ニブルからバイト毎に行われます。
これは想像ですが、PHPの内部ではASCII文字列で格納されていて、表示の時にはそのASCIIコードを任意の文字コード(ここではUTF8)に変換しているのでしょう。厳密に言うと機種依存文字はASCII文字列とは言えないので、PHPでの拡張ASCII文字列という感じでしょうが。そうだとすると、実験はしてないですが、UTF8でも同じ関数で置き換えが出来るはずです。もしこの辺りに詳しい方がいらっしゃいましたら、教えて下さると有難いです。
この意味を完全に勘違いしていたのですが、bin2hexはバイナリデータをASCII形式のHEXで返すと書いてあります。従って、文字データはバイナリデータで保存されています。この時のデータ形式はUTF8です。bin2hexで返ってくるHEXデータは、UnicodeをUTF-8で符号化したものと一致します。'あ'のUTF8コードは0xE38182ですが、``
$ php -r "echo bin2hex('あ').PHP_EOL;"
e38182
の、ような結果になります。