国際文字名への置換
高木です。おはようございます。
先日も書いたように、現在『PHPによるC言語前処理入門』という書籍を執筆中です。
今回は、その書籍の宣伝もかねて、その中の一部の原稿を紹介したいと思います。
今回紹介するのは、執筆中の書籍の中でも比較的あっさりした部分です。
PHPを使ってC言語のソースコードに含まれる多バイト文字を国際文字名に置換する方法を解説しています。
この国際文字名の形式は、C言語に限らず、いくつかの言語で採用されていますので、まったく同じ方法で置換することができると思います。
それでは、以下が現時点での原稿からの抜粋です。
C言語では、ソースファイルに含まれる物理的な多バイト文字を、処理系定義の方法でソース文字集合へ翻訳段階1で変換します。素直に解釈すれば、漢字などの多バイト文字は国際文字名に変換されると考えられます。
ただ、現実的には期待通りに変換されないことも少なくありません。たとえば、GCCはこうした変換が一切行われないようです。
GCCでは多バイト文字から国際文字名への変換こそ行われませんが、国際文字名は認識できます(-fextended-identifiersオプションを付けてコンパイルする必要がありますが)。それであれば、PHPで前処理する際に、多バイト文字から国際文字名に変換してあげればよいのです。
具体例を挙げながら解説することにしましょう。
123456789101112131415 <?phpfunction to_ucn(string $code, string $encoding = 'auto') : string{$utf16 = mb_convert_encoding($code, 'UTF-16LE', $encoding);$result = '';foreach (unpack('v*', $utf16) as $c){if ($c < 0x80)$result .= chr($c);else$result .= sprintf("\\u%04x", $c);}return $result;}上記のサンプルコードで定義しているto_ucn関数は、文字通り国際文字名(universal character name = UCN)に変換するための関数です。元の(多バイト文字混じりの)コードを$code、$codeの文字エンコーディングを$encodingで受け取ります。そして、多バイト文字を国際文字名に変換したコードを返却値として返します。
まず最初に行っているのはUTF-16への変換です。ここでは、便宜上国際文字名は16ビット表現(\uXXXX形式)のみを使うことにします。この形式ではUTF-16の数値そのものを16進数で扱うことになりますので、リトルエンディアンのUTF-16に変換しています。
次にunpack関数を使って1文字ずつバラバラにした結果を配列に格納しています。unpack関数の第1引数では、バイナリ―データから値を取り出すための書式を指定します。’v’というのはリトルエンディアンの16ビット符号無し整数値を取り出す書式です。’v’の後ろについているアスタリスク(*)は同じ書式を末尾まで繰り返すという意味です。これにより、mb_convert_encoding関数によって生成したUTF-16形式のコードを1文字単位でバラバラに分解することができます。
あとは、ループを回して1文字ずつ処理していきます。0x80未満の文字はそのまま、0x80以上の文字は国際文字名に変換しています。厳密なことをいえば、’$’や’@’や’`’といった基本ソース文字集合に存在しない文字も国際文字名に変換したほうがよいのかもしれませんが、まあそこまではいいでしょう。
なお、国際文字名に変換できたとして、さらにはそれが正しくコンパイルできたとしても、デバッガーが対応していない可能性はあります。その場合は残念ながら不便を受け入れるしかありません。