Chomado's Blog
You Are Reading

【docomo雑談対話API】【UTF-8/SJIS】文字コードの扱いについて

0
PHP

【docomo雑談対話API】【UTF-8/SJIS】文字コードの扱いについて


docomoの対話APIについてです.
私はこの素晴らしいAPIのおかげで, 現在製作中のちょまどbot(@chomado_bot)に対話機能を付けることが出来, 毎日楽しんでいます!
でも, 文字コードを(仕様に書いてある) UTF-8 にしても, Shift-JIS で収まらないものを与えると 400 Bad Request でしぬようなので, ここに書いておきます.(2015/1/28現在)
問題が起こった時, 私は自力で考えても全然原因が分からなくて, 詳しい人に教えていただくまでずっと詰んでいたので,
他の人の役に立てばいいなという思いで, ここに, 記しておきます.

【重要 後記2015/3/1】(注) このバグは2015/2/24のdocomo対話APIのアップデートで修正されたっぽいです. でも記念にこの記事は残しておきます.

目次

  1. まとめ/結論
  2. 具体的な修正例
  3. 何があったか時系列順にまとめ
    1. ちょまどbotに対話機能付けたい!(((o(*゚▽゚*)o)))
    2. docomo雑談対話APIを利用したら, botに対話機能実装できた!楽しい!
    3. ところが, ちょまどbotのツイート見てると, たまに, 対話部が空白になっているリプライを返していることがある.
    4. 調べてみたら, どうやら, APIに渡している文字列(ユーザ名とリプライ本文)のどこかに € とか ✗ とか特殊文字が含まれていると, HTTP/1.1 400 Bad Request が返ってくる模様.
    5. API仕様通りにUTF-8で渡していてもダメ. なんで?
    6. 詳しい人が検証してくださった. どうやら Shift-JIS で収まらないものを与えると 400 Bad Request でしぬよう
    7. SJIS で収まらないものは下駄記号『〓』で置換してAPIへ送る修正.
  4. その後の話

まとめ/結論

スクリーンショット 2015-01-29 9.19.18

仕様書.pdf には 『共通仕様』の文字コードとして UTF-8 が指定してあるのですが, 実際は, 内部のどこかが CP932(Windows の Shift-JIS) で動いているようで, € とか ✗ とか特殊な文字を渡すとしにます. (なのでもちろん明示的に UTF-8 変換してもしにます)

APIへ送る文字列(JSON)は, UTF-8にした上で, すべて, SJIS で収まらないものは何か安全な文字(SJISに収まるもの)で置換しましょう.

具体的な修正例

GitHub: class/chat.php
@fetus_hina さんからプルリクくださいました: https://github.com/chomado/chomado_bot/commit/1d2b3494da27b15d1ff2936c7d45e8f5a94ce0d4

この例に使っている,
$user_data は API に渡すデータです

変更前 (= 私が書いていたコード):

private function GetData($context, $nickname, $text)
{
	$user_data = array(
		'utt'       => (string)$text,
		'context'   => (string)$context,
		'nickname'  => (string)$nickname,
	);

変更後:

private function GetData($context, $nickname, $text)
{
	$user_data = array(
		'utt'       => self::sjisSafe((string)$text),
		'context'   => (string)$context,
		'nickname'  => self::sjisSafe((string)$nickname),
	);
/**
 * 与えられた文字列を Shift-JIS で収まる範囲に変換する
 *
 * docomo APIがどうも Shift-JIS で収まらないものを与えると Bad Request で死ぬようなので
 * とりあえず SJIS で収まるもののみを送るようにしてみる
 *
 * @param string $text 変換対象文字列
 * @return string
 */
private static function sjisSafe($text) {
	mb_substitute_character(0x3013); // 変換できない文字をゲタにする
	return mb_convert_encoding(
		mb_convert_encoding($text, 'CP932', 'UTF-8'),
		'UTF-8',
		'CP932'
	);
}

何があったか時系列順にまとめ

1.ちょまどbotに対話機能付けたい!(((o(*゚▽゚*)o)))

↓ ask.fmで来た

2.docomo雑談対話APIを利用したら, botに対話機能実装できた!楽しい!


① 名前,
② 対話部(from docomo対話API),
③ 顔文字
という順序のフォーマットにした.

↓ 煽り性能も高い

スクリーンショット 2015-01-28 14.09.28

3.ところが, ちょまどbotのツイート見てると, たまに, 対話部が空白になっているリプライを返していることがある.

つまり, なんらかの条件で, docomo対話APIから取ってきた対話部がNULLになっているみたい. (対話APIから何も返ってきていないということ.)

↓ 対話部が何もない. (下の顔文字(((o(*゚▽゚*)o)))は埋め込みでAPIのじゃない)

4.調べてみたら, どうやら, APIに渡している文字列(ユーザ名とリプライ本文)のどこかに € とか ✗ とか特殊文字が含まれていると, HTTP/1.1 400 Bad Request が返ってくる模様.

5.API仕様通りにUTF-8で渡していてもダメ. なんで?

6.詳しい人(@fetus_hina)が検証してくださった. どうやら Shift-JIS で収まらないものを与えると 400 Bad Request でしぬよう


(後から注: @fetus_hina さん「※このコードにはバグがあり正しく対応できていません」)

7. SJIS で収まらないものは下駄記号『〓』で置換してAPIへ送る修正.

@fetus_hina さんからプルリクくださいました: https://github.com/chomado/chomado_bot/commit/1d2b3494da27b15d1ff2936c7d45e8f5a94ce0d4

その後の話

これら一連のことを, ベテランのエンジニアさんに話したら,
「ああ, 文字コードのゴタゴタね. よくある話だから, まあ, いい経験になったんじゃない?(^o^)」

よくある話らしい!!


Madoka Chomado (ちょまど)

千代田まどかです。よく「ちょまど」と呼ばれます。Microsoft 社員。文系出身プログラマ兼マンガ家です。

(3) Comments

  1. […] 【docomo雑談対話API】【UTF-8/SJIS】文字コードの扱いについて | ちょまど帳 docomoの対話APIについてです. 私はこの素晴らしいAPIのおかげで, 現在製作中のちょまどbot(@chomado_bot)に対話機能を […]

  2. 相沢陽菜 says:

    ※ごちゅうい:
    2015-02-24 以降、この問題に対する修正が行われており、現在はこの対応は必要ありません。
    ただし、この対応は「CP932で表せない文字を消す」ことで行われているように推測できる挙動を行うため、「変な文字」を挟む位置によっては文意が変わる可能性がありますし、「変な文字」のみで構成されるテキストを送信すると空の応答が返ってきたりします。

    ちょまどbotからもこの問題に対応するコードは削除されています。
    https://github.com/chomado/chomado_bot/commit/dbc00b6c286b17e3194d9365ab3e0df7ef291807

    1. わあ! ひなさんいつもありがとうございます!

ちょまど へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 が付いている欄は必須項目です