文字化け復元ページを作ったよ

これは SmartHR アドベントカレンダーの4日目の記事です。

qiita.com

といっても内容は会社とは関係ありません。


文字化け復元ページを作った。

tmtms.net

使い方

最初に「繧ゅ§縺ー縺代r縺オ縺上£繧薙☆繧九h」という文字化けした文字列が入力されてる。

「復元」ボタンを押すと、それが復元されて「もじばけをふくげんするよ」という文字列が表示される。

さらに「文字化け」ボタンを押すと、文字化けされた文字列が表示される。 これが元の「繧ゅ§縺ー縺代r縺オ縺上£繧薙☆繧九h」と同じなので、正常に復元されたことがわかる。

一番上の文字列を消して「豎滓虻蟾昴さ繝翫Φ縺ョ豁」菴薙・蟾・阯、譁ー荳・」を入れて「復元」を押してみる。 「江戸川コナンの正体\xE3?工藤新\xE4\xB8?」となる。 文字化け文字列中の「」の部分は情報が落ちてしまった部分。情報が落ちてしまったので完全には復元できない。

まあ、それ以外の文字列から「江戸川コナンの正体は工藤新一」と想像できるので、そのように入力して「文字化け」を押してみると、「豎滓虻蟾昴さ繝翫Φ縺ョ豁」菴薙・蟾・阯、譁ー荳・」となり元の文字列と同じだから、「江戸川コナンの正体は工藤新一」だった可能性が高いと判断できる。

このように情報が落ちてしまってる場合は前後の文脈からある程度推測するしかない。 途中の「\xE3?」は文字コード的にはたとえば「」でも成立するし、末尾の「\xE4\xB8?」は「」でも成立するのでもしかしたら「江戸川コナンの正体ね工藤新三」の可能性もある。

文字化けと復元方法

このページは、UTF-8 で書かれた文字列を CP932(Windows-31J)で表示しようとして文字化けしてしまった文字列に対応している。

それを復元しようとしたら、該当文字を CP932 に変換し、そのバイト列を UTF-8 として表示すればいい。 「・」は情報が落ちてしまった文字なので、そのまま扱うとおかしなことになるので、たとえば「?」に変換しておく。

Ruby で書くならこんな感じ。

str.tr('', '?')             # 「・」を「?」に変換
  .encode('cp932', undef: :replace, invalid: :replace, replace: '?')
                              # CP932 に変換(変換できない文字は「?」にする)
  .force_encoding('utf-8')    # バイト列を強制的に UTF-8 とする

文字化けさせるには、こんな感じで。

str.force_encoding('cp932')    # バイト列を強制的に CP932 とする
  .encode('utf-8', undef: :replace, invalid: :replace, replace: '')
                               # UTF-8 に変換(変換できない文字は「・」にする)

しくみ

前からこういうページを作ろうかと考えてたんだけど、このためにわざわざサーバーサイドを作るのはちょっとなーと思って、かといって JavaScript でこれを実現するのはムリだと思ったので諦めてた。

Ruby が WebAssembly で動くようになったので、サーバーサイドを作らなくても実現できるようになったんで作ってみた。

https://github.com/ruby/ruby.wasm/ にある通り、

<script src="https://cdn.jsdelivr.net/npm/ruby-head-wasm-wasi@0.5.0/dist/browser.script.iife.js"></script>
<script type="text/ruby">
  ここが Ruby スクリプト
</script>

ってするだけで Ruby がブラウザ上で動く。https://tmtms.net/mojibake.html のソースもそうなってる。

標準入出力等はブラウザのコンソールになる。 DOM 操作は js ライブラリ経由で JavaScript を操作する必要がある。 メソッド名がスネークケースじゃなくて JavaScript 風のキャメルケースなのでちょっと気持ち悪いけど、普通に DOM 操作できる。

あと、スクリプトエンコーディングが UTF-8 じゃなくて ASCII-8BIT になってる。今回文字化けページを作るのにちょっとハマった。

ブラウザ上で動くプログラムを JavaScript ではなく Ruby でそのまま書けるのはかなり体験が良い。もっと書いていこう。