ブラウザの文字コード誤認識と対策
サーブレットによる Web 開発において日本語 (Shift_JIS コード) の文字化けはポピュラーな話題の一つである。サーバ側での文字化けについてはサーブレットの基本としてあちこちで紹介されているので改めて述べる必要もないだろう。あれらは再現性があり試行錯誤もできる。
しかし、ブラウザの文字エンコーディング誤認識は偶発的で再現性がなく、それどころかしばしば問題そのものがサーバ側の文字化けと混同されている事がある。不適切な方法を紹介しているサイトも最近目に付くのでここで正しておこうと思う。
なお、ここで記述している 「ブラウザ」 とは主に Internet Explorer の事であり、全てのブラウザを意味しているわけではない。
文字エンコーディングの指定
JSP の中で文字エンコーディングを指示する場所は 3 つある。それぞれ用途や目的が異なっているので、まずはそれらを整理しよう。
<%@ page language="java" contentType="text/html; charset=MS932"※1 pageEncoding="MS932"※2 %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS"※3> ...
※1 は HTTP の Content-Type ヘッダである。つまり、「HTTP通信上で」 この HTML がどんな文字エンコーディングを使用しているかをクライアントに伝えている。同時に、どんな文字エンコーディングで送ればよいかを JSP プロセッサに示している。
次に ※2 であるが、これは単にこの JSP ファイルがどのエンコーディングで保存されているかを示しているかに過ぎない。例えば Windows (Shift_JIS) で作成した JSP を Unix (EUC-JP) のサーバで動かすような場合、JSP が Shift_JIS で記述されていることを明示してやらないといけない。つまりこれは JSP プロセッサが参照するエンコーディングである。
最後に ※3 であるが、これは 「HTML 上で」 この HTML がどんな文字エンコーディングを使用しているか示している (プロバイダ上に間借りしている個人サイトなどでは HTTP ヘッダを操作できないため HTML 上で記述できる必要がある)。名前の通り、記述してある内容が HTTP の Content-Type ヘッダと等価ということを示している。
ここで抑えておきたいのは、ブラウザは ※1 の文字エンコーディングが認識できなかった場合に ※3 を使用するということである。 どの指示を誰が使用するかをまとめてみよう。
用途 | ブラウザ | JSPプロセッサ | |
※1 | HTTPヘッダのContent-Type | ○受信したページに適用 | ○送信するページに適用 |
※2 | JSP ファイルの文字エンコーディング | - | ○JSPファイル読み出し時に適用 |
※3 | ※1の振り替え手段 | ○受信したページに適用 (※1を認識できなかった場合) |
- |
Shift_JIS の問題
ここで Shift_JIS に関する問題を整理してみよう。
- IANA 規定の厳密な Shift_JIS には「~」「-」などの文字が定義されていない。
- JSP (Java) は規定に厳格である。Shift_JIS を指定すると上記の文字が全て ? に置き換えられてしまうため、代わりに Windows-31J (=MS932) などを指定してやらなければならない。
- ブラウザ (Windows) はルーズである。Shift_JIS は MS 拡張 Shift_JIS と同等に扱われるため上記の文字も正しく表示する。だが Windows-31J が MS 拡張 Shift_JIS である事をブラウザは知らない。
- ブラウザは文字エンコーディングが認識できない場合に自動認識の動作を行い、しばしば誤認識の問題を引き起こす。
文字エンコーディング誤認識の根本的な防止方法が自動認識となるような状況を作らないことであることは明白である。ここで我々は前述のエンコーディング指定場所とあわせて、どこに、誰が参照する、どんなエンコーディングを指示しなければならないかを注意深く考える必要がある。
- ※2 は Java に向けた指示なので Java が認識できる文字エンコーディングを指定する。
- ※3 は Java からは参照されないので、ブラウザが認識できる Shift_JIS を指定する。
- ※1 は Java とブラウザの両方から参照するが、JSP の送信文字エンコーディングはここでしか指示できず、ブラウザは ※3 による振り替え手段があるため、ここでは Java のエンコーディングを優先して Windows-31J を指定する。
最後の項目は苦肉の策であるが、これは JSP (サーブレット) の仕様上諦めるしかない。 iso-8859-x 圏の民族が考えた仕様では珍しいことではない。
結論
ここまでの諸事を考慮すると、JSP から Shift_JIS で HTML を送信すための指示は以下が適切と考えられる。
場所 | 意味 | 対象 | 例 |
※1: <%@ page %> 命令の contentType 属性 |
送信文字エンコーディング | JSP (Java) | Windows-31J |
※2: <%@ page %> 命令の pageEncoding 属性 |
JSP の保存形式 | JSP (Java) | Windows-31J |
※3: <meta> 要素の content 属性 |
受信文字エンコーディング | ブラウザ | Shift_JIS |
最も理想的なのは Shift_JIS 系を捨てて全て UTF-8 で統一することである。UTF-8 であればエンコーディングが統一できる上に Unicode からのコード変換処理も軽くて済む。しかし、マルチバイト中心のページでは転送量が若干増える、携帯電話や PDA などのチープなブラウザに対応させる必要がある、ノウハウがないなどの理由でプロジェクトとして Shift_JIS を選択せざるをえないこともあるだろう。
誤認識の再現方法
IE6 の文字エンコーディング誤認識を確実に再現させる一つの方法が分かったので記しておく。手順は以下の通り。
- まず、特定のページをブラウザの認識できる文字エンコーディングを指定して正しく表示させる。
- ページをリロードする。この時、
- 1. で使用した文字エンコーディング以外でページをエンコードする。
- Content-Type の文字エンコーディングは省略するか、またはブラウザが認識できない文字エンコーディングにする。
重要なのは 「同じ URL でエンコーディングが変わる事」、そして 「リロード」 のようだ (リンククリックでもフォームの POST でもなく何故リロードなのかは謎)。どうやらこの状況下の自動認識処理では以前に適用したエンコードが優先されていると思われる。サーブレットコンテナ再起動中にブラウザでアクセスして Apache のエラーページが表示され、起動完了後にそのままリロードし、エラーではないが文字化けしているという、私がたまに見かけていた状況というのがまさにこれなのだろう (Apache のエラーページは us-ascii)。
エンコーディングの切り替えは response.setHeader("Content-Type", "text/html;charset="+encoding)
記述すれば、まぁここまで読める人なら JSP で簡単に書くことが出来るだろう。
念のため、これは再現方法の一つなのであって、動的にエンコーディングを切り替えなければ問題ないという意味ではない。それと FireFox 2 はこの手順では再現しない。
Windows-31J の嘘
Windows-31J ─ Microsoft コードページ 932 のことである。J2SE 1.4 から導入され、それ以前まで使用されていた MS932 のエイリアスとなっている。いわゆる NEC 拡張文字、IBM 拡張文字も含んでおり 「Windows の Shift_JIS」 と言えばこれを指しているに等しい。Windows という名が入っていることからローカルな名前なのかと思いきや、実は IANA に登録されている、れっきとした 「世界的に認められた文字コード名」 なのである。
では Windows-31J を指定すれば Java もブラウザも文字化けや誤認識がなくなるのだろうか。少なくとも IE6 にとってそれは嘘である。以下は前述の再現方法を実現するために 「UTF-8」 「Shift_JIS」 「Windows-31J」をランダムに切り替える JSP である。何度か RELOAD をクリックすれば (JavaScript 内でリロードしている)、UTF-8 → Windows-31J のパターンでだけ誤認識が発生することが分かるだろう。これは IE6 が Windows-31J を正しく認識できていないことを意味している。
Windows-31J が IANA に正式登録されたのははるか前世紀の事である。このため Windows-31J 対応が IE6 に間に合わなかったとは考えにくく、むしろ対応する必要がないという MS の意志が働いたと考える方が自然である。今後の IE バージョンアップでの対応は期待しないほうが良いだろう。
一方で 「Shift_JIS だといくつかの文字が化けるので、IANA にも登録されている Windows-31J を使いましょう」 と勧めているサイトがやたらと多いのも事実である。もちろんこれらがサーバ側での文字化けを解消するのが目的だということは分かるのだが、少々配慮に欠けているのではないだろうかと思わざるを得ない。
書き方スクリプト
それで結局、あなたの JSP はどう記述すればよいのか? それは以下のスクリプトで決めてください。
<%@ page language="java" contentType="text/html; charset=Windows-31J" pageEncoding="Windows-31J" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS"> ...

C/C++ 使いだった 1996年、運命の Java と出会い現在に至る。のらアーキテクト。

- 移転でーす (12/31)
- Android 様は日本語ファイル名でダウンロードできない御様子 (12/03)
- Scientific Linux 6.1 導入 (11/30)
- Windows で Rails3 の動作環境構築を諦める3つの理由 (10/11)
- "Could not find a JavaScript runtime" と Rails 3.1 起動に失敗する件 (10/11)
- Ruby 1.9 + Rails 3.1 + Passenger サーバ構築 (09/21)
- Linux Xen 上で Windows 8 Developer Preview を動かす (09/15)
- Ruby 1.9.2 ソースビルド (09/12)