at_yasu's blog

ロード的なことを

PHP のSJIS

増永 玲 さんが、おー、となるライブラリを作ってはって、確かにたまに知りたいけど見なかったことにしてる所だなぁ、と思った。と同時に別のことでちょっと気になったこと。

玲さんが作られたライブラリとは全く関係ありませんよっと。そんで眠たいので雑。

ややこしいことに
PHP でサポートされる文字エンコーディングにはエイリアスがあって
SJIS の場合は Shift_JIS, SHIFT-JIS, x-sjis と
それぞれの大文字小文字違いも同じ扱いなので
そのへんを isSjis() というメソッドのあたりで処理してます。

の x-sjis と SHIFT-JIS と Shift_JIS の扱いがそれぞれ大文字小文字違いも同じ扱いってのが少し気になって、Pythonでも CP932shift_jisx0213 とか微妙な差はあったはずよなぁ、と思ったのです。で、実際 Shift_JIS と SHIFT-JIS はどやつを見てるのかしらと気になるわけでしす。

で、こーいうのはドキュメントよりソース見たほうが速いっつー事で見て行ったら、libmbflってのに行き着くわけで、さぁどうなってるかな、と。

とりあえず PHP から見える、対応しているエンコード一覧はこんな感じ。

php-shell

php > print_r(mb_list_encodings());
Array
(
    [0] => pass
    [1] => auto
    [2] => wchar
...
    [30] => SJIS
    [31] => eucJP-win
    [32] => SJIS-win
    [33] => CP932
    [35] => JIS
    [36] => ISO-2022-JP
    [37] => ISO-2022-JP-MS
    [38] => Windows-1252
    [39] => Windows-1254
    [40] => ISO-8859-1

ではでは、ソース見学。ざっと見たところ「mbfl_name2encoding」っていう関数がエンコード名解決をしている気配。

サンプルとして、mb_convert_encoding の実態

php-src/ext/mbstirng/mbstring.c

2948 MBSTRING_API char * php_mb_convert_encoding(const char *input, size_t length, const char *_to_encoding, const char *_from_encodings, si     ze_t *output_len TSRMLS_DC)
...
2963     /* new encoding */
2964     if (_to_encoding && strlen(_to_encoding)) {
2965         to_encoding = mbfl_name2encoding(_to_encoding);
2966         if (!to_encoding) {
2967             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown encoding \"%s\"", _to_encoding);
2968             return NULL;
2969         }
2970     } else {
2971         to_encoding = MBSTRG(current_internal_encoding);
2972     }

で、だ、「mbfl_name2encoding」はというと、

php-src/ext/mbstirng/libmbfl/mbfl/mbfl_encoding.c

133 static const mbfl_encoding *mbfl_encoding_ptr_list = {
134     &mbfl_encoding_pass,
135     &mbfl_encoding_auto,
136     &mbfl_encoding_wchar,
137     &mbfl_encoding_byte2be,
138     &mbfl_encoding_byte2le,
...
167     &mbfl_encoding_sjis_open,
168     &mbfl_encoding_sjis_docomo,
169     &mbfl_encoding_sjis_kddi,
170     &mbfl_encoding_sjis_sb,
171     &mbfl_encoding_sjis_mac,
172     &mbfl_encoding_sjis2004,
173     &mbfl_encoding_utf8_docomo,
174     &mbfl_encoding_utf8_kddi_a,
175     &mbfl_encoding_utf8_kddi_b,
176     &mbfl_encoding_utf8_sb,
177     &mbfl_encoding_cp932,
178     &mbfl_encoding_cp51932,
179     &mbfl_encoding_jis,
180     &mbfl_encoding_2022jp,
...
210     &mbfl_encoding_koi8r,
211     &mbfl_encoding_koi8u,
212     &mbfl_encoding_armscii8,
213     &mbfl_encoding_cp850,
214     &mbfl_encoding_jis_ms,
215     &mbfl_encoding_2022jp_2004,
216     &mbfl_encoding_2022jp_kddi,
217     &mbfl_encoding_cp50220,
218     &mbfl_encoding_cp50220raw,
219     &mbfl_encoding_cp50221,
220     &mbfl_encoding_cp50222,
221     NULL
222 };
...
226 mbfl_name2encoding(const char *name)
227 {
228     const mbfl_encoding *encoding;
229     int i, j;
230 
231     if (name == NULL) {
232         return NULL;
233     }
234 
235     i = 0;
236     while ((encoding = mbfl_encoding_ptr_list[i++]) != NULL){
237         if (strcasecmp(encoding->name, name) == 0) {
238             return encoding;
239         }
240     }
241 
242     /* serch MIME charset name */
243     i = 0;
244     while ((encoding = mbfl_encoding_ptr_list[i++]) != NULL) {
245         if (encoding->mime_name != NULL) {
246             if (strcasecmp(encoding->mime_name, name) == 0) {
247                 return encoding;
248             }
249         }
250     }
251 
252     /* serch aliases */
253     i = 0;
254     while ((encoding = mbfl_encoding_ptr_list[i++]) != NULL) {
255         if (encoding->aliases != NULL) {
256             j = 0;
257             while ((*encoding->aliases)[j] != NULL) {
258                 if (strcasecmp((*encoding->aliases)[j], name) == 0) {
259                     return encoding;
260                 }
261                 j++;
262             }
263         }
264     }
265 
266     return NULL;
267 }

長ったらしいけど仕方ない。

さてさて、ここで比較されているのは「mbfl_encoding_ptr_list」の値。比較には「strcasecmp」関数使われているので、大文字小文字は関係なしになってますね。これで疑問一つ解消。 では「mbfl_encoding_ptr_list」とはというと、上でconstで定期されてますねっと。こやつ、殆ど PHPの「mb_list_encodings」関数の返り値と似てます。

で、だ、この「mbfl_encoding_ptr_list」の中にある構造体は「mbfl_encoding」という構造体らしく、こやつの要素はこんな感じ。

php-src/ext/mbstirng/libmbfl/mbfl/mbfl_encoding.h

134 typedef struct _mbfl_encoding {
135     enum mbfl_no_encoding no_encoding;
136     const char *name;
137     const char *mime_name;
138     const char *(*aliases);
139     const unsigned char *mblen_table;
140     unsigned int flag;
141 } mbfl_encoding;

flagっつーのが何なのかわからないけど、まぁ大体わかりますよね?nameが実際名、aliases がエイリアス名、mime_name が通変的な名前?

で、実際にはこんな感じで使われてる。

php-src/ext/mbstirng/libmbfl/filters/mbfilter_cp932.c

61 static const char *mbfl_encoding_cp932_aliases = {"MS932", "Windows-31J", "MS_Kanji", NULL};
62 
63 const mbfl_encoding mbfl_encoding_cp932 = {
64     mbfl_no_encoding_cp932,
65     "CP932",
66     "Shift_JIS",
67     (const char *(*))&mbfl_encoding_cp932_aliases,
68     mblen_table_sjis,
69     MBFL_ENCTYPE_MBCS | MBFL_ENCTYPE_GL_UNSAFE
70 };

C の構造体を見るのがかなり久しぶりで、こういう書き方だっけな、という状態ですが、まぁ、コレだと「name->CP932」「mime_name->Shift_JIS」「aliases -> {"MS932", "Windows-31J", "MS_Kanji", NULL}」

で、だ、「SJIS, Shift_JIS, SHIFT-JIS, x-sjis」それぞれ違いはと、いう話なんですが、sjisの処理系は5つに分かれておりまして、mbfilter_(cp932|sjis|sjis_2004|sjis_mac|sjis_mobile|sjis_open) となっておりますよっと。

それぞれ必要部分だけ書きだしてみるとこんな感じ

php-src/ext/mbstirng/libmbfl/filters/mbfilter_cp932.c

61 static const char *mbfl_encoding_cp932_aliases = {"MS932", "Windows-31J", "MS_Kanji", NULL};
62 
63 const mbfl_encoding mbfl_encoding_cp932 = {
64     mbfl_no_encoding_cp932,
65     "CP932",
66     "Shift_JIS",
67     (const char *(*))&mbfl_encoding_cp932_aliases,
68     mblen_table_sjis,
69     MBFL_ENCTYPE_MBCS | MBFL_ENCTYPE_GL_UNSAFE
70 };

php-src/ext/mbstirng/libmbfl/filters/mbfilter_sjis.c

64 static const char *mbfl_encoding_sjis_aliases = {"x-sjis", "SHIFT-JIS", NULL};
65 
66 const mbfl_encoding mbfl_encoding_sjis = {
67     mbfl_no_encoding_sjis,
68     "SJIS",
69     "Shift_JIS",
70     (const char *(*))&mbfl_encoding_sjis_aliases,
71     mblen_table_sjis,
72     MBFL_ENCTYPE_MBCS | MBFL_ENCTYPE_GL_UNSAFE
73 };

php-src/ext/mbstirng/libmbfl/filters/mbfilter_sjis_2004.c

48 static const char *mbfl_encoding_sjis2004_aliases = {"SJIS2004","Shift_JIS-2004", NULL};
49 
50 const mbfl_encoding mbfl_encoding_sjis2004 = {
51     mbfl_no_encoding_sjis2004,
52     "SJIS-2004",
53     "Shift_JIS",
54     (const char *(*))&mbfl_encoding_sjis2004_aliases,
55     mblen_table_sjis,
56     MBFL_ENCTYPE_MBCS | MBFL_ENCTYPE_GL_UNSAFE
57 };

php-src/ext/mbstirng/libmbfl/filters/mbfilter_sjis_mac.c

47 static const char *mbfl_encoding_sjis_mac_aliases = {"MacJapanese", "x-Mac-Japanese", NULL};
48 
49 const mbfl_encoding mbfl_encoding_sjis_mac = {
50     mbfl_no_encoding_sjis_mac,
51     "SJIS-mac",
52     "Shift_JIS",
53     (const char *(*))&mbfl_encoding_sjis_mac_aliases,
54     mblen_table_sjis,
55     MBFL_ENCTYPE_MBCS | MBFL_ENCTYPE_GL_UNSAFE
56 };

php-src/ext/mbstirng/libmbfl/filters/mbfilter_sjis_mobile.c

47 static const char *mbfl_encoding_sjis_docomo_aliases = {"SJIS-DOCOMO", "shift_jis-imode", "x-sjis-emoji-docomo", NULL};
48 static const char *mbfl_encoding_sjis_kddi_aliases = {"SJIS-KDDI", "shift_jis-kddi", "x-sjis-emoji-kddi", NULL};
49 static const char *mbfl_encoding_sjis_sb_aliases = {"SJIS-SOFTBANK", "shift_jis-softbank", "x-sjis-emoji-softbank", NULL};
50 
51 const mbfl_encoding mbfl_encoding_sjis_docomo = {
52     mbfl_no_encoding_sjis_docomo,
53     "SJIS-Mobile#DOCOMO",
54     "Shift_JIS",
55     (const char *(*))&mbfl_encoding_sjis_docomo_aliases,
56     mblen_table_sjis,
57     MBFL_ENCTYPE_MBCS | MBFL_ENCTYPE_GL_UNSAFE
58 };
59 
60 const mbfl_encoding mbfl_encoding_sjis_kddi = {
61     mbfl_no_encoding_sjis_kddi,
62     "SJIS-Mobile#KDDI",
63     "Shift_JIS",
64     (const char *(*))&mbfl_encoding_sjis_kddi_aliases,
65     mblen_table_sjis,
66     MBFL_ENCTYPE_MBCS | MBFL_ENCTYPE_GL_UNSAFE
67 };
68 
69 const mbfl_encoding mbfl_encoding_sjis_sb = {
70     mbfl_no_encoding_sjis_sb,
71     "SJIS-Mobile#SOFTBANK",
72     "Shift_JIS",
73     (const char *(*))&mbfl_encoding_sjis_sb_aliases,
74     mblen_table_sjis,
75     MBFL_ENCTYPE_MBCS | MBFL_ENCTYPE_GL_UNSAFE
76 };

php-src/ext/mbstirng/libmbfl/filters/mbfilter_sjis_open.c

61 static const char *mbfl_encoding_sjis_open_aliases = {"SJIS-open", "SJIS-ms", NULL};
62 
63 const mbfl_encoding mbfl_encoding_sjis_open = {
64     mbfl_no_encoding_sjis_open,
65     "SJIS-win",
66     "Shift_JIS",
67     (const char *(*))&mbfl_encoding_sjis_open_aliases,
68     mblen_table_sjis,
69     MBFL_ENCTYPE_MBCS | MBFL_ENCTYPE_GL_UNSAFE
70 };

長ったらしいです。ですがフォーマットが統一されているので、いいですよね?

さてさて、「mbfl_name2encoding」の処理を見ると、

  1. 「mbfl_encoding_ptr_list」を一から見て、中の name を見て比較、ありゃ返す
  2. 「mbfl_encoding_ptr_list」を一から見て、中の MIME を見て比較、ありゃ返す
  3. 「mbfl_encoding_ptr_list」を一から見て、中のエイリアスを見て比較、ありゃ返す。
  4. 見つからん時は NULL

で、「mbfl_encoding_ptr_list」はというと、上記に書いた感じで順番に並んでます。

Shift-jis 周りだけを見るとこんな感じ

php-src/ext/mbstirng/libmbfl/mbfl/mbfl_encoding.c

164     &mbfl_encoding_sjis,
...
167     &mbfl_encoding_sjis_open,
168     &mbfl_encoding_sjis_docomo,
169     &mbfl_encoding_sjis_kddi,
170     &mbfl_encoding_sjis_sb,
171     &mbfl_encoding_sjis_mac,
172     &mbfl_encoding_sjis2004,
...
177     &mbfl_encoding_cp932,

そんなわけで

  1. 「Shift-JIS」と銘打った場合、「mbfl_name2encoding」の処理手順から見て、エイリアスに「SHIFT-JIS」がある「SJIS」(mbfl_encoding_sjis)と等価になります。
  2. Shift_JIS」と銘打った場合、「mbfl_name2encoding」の処理手順から見て、MIMEに当てはまるので「SJIS」と等価になります。
  3. 「x-sjis」と銘打った(ry

そんな訳で、パフォーマンスの差はありますが、「SJIS, Shift_JIS, SHIFT-JIS, x-sjis」は変換する文字コードの種類は同じです。

ちょっとでもパフォーマンスが気になる、という人は「Shift-JIS」や「Shift_JIS」や「x-sjis」を「SJIS」に変換して投げてやればいいと思うんです。

そんじゃーね