44
47

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

json_encode()でjsonをUTF-8に変換する方法(サロゲートペア対応)

Last updated at Posted at 2014-07-13

json_encode()で出力したjsonってユニコードエスケープされているのですが、出力したjsonを手でメンテしたりすることを考えると、utf-8になっている方がうれしいわけです。
PHP6だとunicode_encode()なるステキ関数が用意されているみたいですが、PHP5だと自前で変換するしかない。
http://d.hatena.ne.jp/iizukaw/20090422 というすばらしい記事があったのでここのunicode_encode()を使ってjsonのutf-8化をしていたのですが。

##問題
サロゲートペアという、1つの文字をunicode文字2文字分(4バイト)で表現する文字があって、上のunicode_encode()だとこれらの文字をうまいこと変換できませんでした。
正規表現"/\\\\u([0-9a-zA-Z]{4})/"でユニコード文字をひとつずつ取り出してpack()してutf-8への変換をしているので、ユニコード2文字で1文字を表現するサロゲートペアがうまいこと変換できないんでしょうね。

##解決
もうちょっとキレイなやり方がありそうな気がするのですが、こうやって直しました。
まずは正規表現を"/((?:\\\\u[0-9a-zA-Z]{4})+)/"にしてユニコード文字をひとまとめにしてゲットできるようにします。
で、受け取った文字を変換するところで

function encode_callback($matches) {
  // \uいらない
  $tmp = preg_replace("/\\\\u/", "", $matches[1]);
  $fields = str_split($tmp, 4);
  $s = "";
  // 連結
  foreach ($fields as $field) {
    $s .= pack("H*", $field);
  }
  // ひとつながりのユニコード文字列をutf-8に変換
  // !これだとサロゲートペアもうまいこと解釈してくれるみたい!
  $char = mb_convert_encoding($s, "UTF-8", "UTF-16");

  return $char;
}

とすることで無事変換できるようになりましたとさ。

2016.03.01追記

ものすごく長い文章をこの方法で変換すると、なんと php 自体がクラッシュしました。

// ものすごく長い日本語テキスト
$str = "";
for ($i = 0; $i < 100000; $i++) {
	$str .= "あいうえお";
}

$jsonStr = json_encode($str);
$jsonStr2 = unicode_encode($jsonStr); // ★ ここで死亡

おとなしくPHPのバージョンをあげるのが良さそうです。

44
47
6

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
44
47

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?