はてダロ for nScriptのコードを見て

id:hyukiさんの本家サイトの情報を見て、はてダラnScript版を開発されている方がいらっしゃるのを知る。どなどなさん(id:donayama)、お疲れ様です。お互い精進しましょう。

で、そのどなどなさんがid:donayama:20040902にて文字化けで悩んでおられるご様子。う〜ん、文字コード変換機能は搭載してないらしいので、化けるなんてことあるかなぁ…でやっぱりソースを眺めることに。

はてダラ本家版では編集画面と同じようにform-data形式で送信してますが、どなどなさんのはurlencodedで送信してますね。この時の違いってbody部分のエンコードにあって、urlencodedではその名の通りURLエンコードされるんですが、form-dataの場合はフォームに入力したデータそのまま、なんの変換もかけずに送ってしまいます。マルチパートの中身は全部バイナリ、みたいな感覚ですか。

なので、そのURLエンコードしている部分(httpForm.ns)を見ると…

if(Chr(byteArray[i]) =~ "\W"){
  answer = answer + "%%" + mid(hex, byteArray[i] / 16, 1) + mid(hex, byteArray[i] % 16, 1);
}
else{
  answer = answer + Chr(byteArray[i]);
}

ああなるほど、ここで化けますね。私も通った道です(笑)

URLエンコードは本来、「URL文字列として許容されていない文字をエンコードする」のが正しいです。なのでここでは許容されない文字コードを判定し、エンコードする方法を取っています。
一見正しいように見えますが、実は2バイト文字の2バイト目には1バイトデータとして見れば許容される文字コードが含まれる場合があるので注意が必要です。それでも送信データを受ける側がそれを考慮してデコードしてくれればいい話なのですが…例外として1つだけ、どうにもならない文字コードがあります。それは'\'(バックスラッシュ、0x5C)です。

これが含まれていたりすると、処理をしているスクリプトエンジンによっては(というか大抵)文字列スタック中で「その直後の文字をエスケープする文字」と認識されてしまい、文字列中から1バイト分が消えてその前1バイトから末尾までのデコード結果がオリジナルと違ってきてしまうのです。
id:donayama:20040902では「タイトルが化ける」とのことだったのでバイナリを調べたところ、「能」の2バイト目が 0x5C でモロにここで言及した状態になっているようです。
無理に変換する文字を選択せずに、全てのバイトデータをエンコードしてしまうことで解消すると思われます。

私の場合は直接この問題にぶち当たったことはない(やる前に知識として知ってた)んですが、実はURLエンコードにはもう一つ特殊仕様がありまして…「空白(0x20)は '+' に変換する」というもので、xyzzy のURLエンコード関数では '+' は変換されないんですが、urlencodedでもってサーバが受け取るとこれを ' ' に変換してしまうため、結果的に日記に '+' が書けない、という不具合が発生しました。

結局はURLエンコードした後に独自に '+' を '%2B' へ変換することで対応できましたけど…いやぁ難しいねぇと思った瞬間でした。