JSP/サーブレット・プログラミングで誰もが一度は遭遇するトラブルが文字化けだ。予期せぬ文字化け発生に、デバックに苦労した経験を持つ読者も多いだろう。本連載では、JSP/サーブレットにおける文字列の扱いの基礎を復習した上で、文字化けの解決策を要点よく解説していく予定だ。(編集部)
本記事は2004年に執筆されたものです。Javaの文字化け全般の最新情報は@IT Java Solution全記事一覧のカテゴリ「トラブル・問題解決/ノウハウ/文字化け」をご参照ください。
解答:HTTPのContent-Typeヘッダです
まずは、Webにおける文字コードの扱いをおさらいしておこう。HTML 4.01仕様では、Webブラウザが以下の優先順位で文字コードを決定することを規定している。
Webサーバが送信するHTTPヘッダの中には、送信内容がどのような素性のコンテンツなのかをWebブラウザに教えるために、以下のようなContent-Typeヘッダを通じてコンテンツのメディアタイプ(下記例ではtext/html)を明示する。
Content-Type: text/html; charset=Shift_JIS
これによりWebブラウザは、受信したコンテンツがHTML文書なのか画像ファイルなのか、もしくは外部アプリケーションやプラグインで開くべきファイルなのかを判定する。
WebブラウザがHTML文書の文字コードを判定する際には、このメディアタイプのcharsetパラメータを最優先に参照する。もしcharsetパラメータやContent-Typeヘッダそのものが存在しない場合は、HTML文書内にMETA宣言が存在するか調べる。それでも文字コード情報が得られなければ、HTML文書の内容からの推測によって文字コードを自動判別する仕組みだ。
よって文字コードをWebブラウザに確実に伝えるには、Webサーバが送信するContent-Typeヘッダを使えばよい。逆にいえば、HTML文書内でMETA宣言を指定したとしてもムダになるケースが多い。例えば、META宣言で文字コードを指定する以下のようなJSPページを作成してみよう。
<%@ page language="java" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS" /> <title>テスト</title> </head> <body> テストです。 </body> </html>
このJSPページをサーブレット・コンテナTomcat(Tomcat 5.0.28)で出力すると、図1のように日本語全体が文字化けしてしまう。
このように、JSPファイル内のMETA宣言による文字コード指定はうまく働かない。なぜなら、TomcatはContent-Typeヘッダにデフォルトの文字コードであるISO-8859-1を指定するため、META宣言よりもそちらが優先されてしまうのである。
こうしたトラブルを避けるには、出力するHTML文書の文字コードをTomcatにきちんと伝える必要がある。具体的には、以下の2つの方法がある。
JSPの場合は、「pageディレクティブ」で文字コードを正しく記述しておく(詳しくは後述)。このpageディレクティブさえ指定しておけばMETA宣言による指定は不要だ。またサーブレットの場合は、HttpServletResponseオブジェクトのsetContentTypeメソッドを用いて、サーブレットが出力するコンテンツの文字コードを明示すればよい。
res.setContentType("text/html; charset=Windows-31J");
ちなみに、Apache HTTPサーバとTomcatを連携させる場合、Apacheのhttpd.conf側で以下のようなデフォルトの文字コードが指定されていると、そちらが優先されてContent-Typeヘッダに出力されてしまう。
AddDefaultCharset ISO-8859-1
よってApache利用時は、この行をコメントアウトしておこう。
解答:contentType属性とpageEncoding属性を記述します
JSPファイルの文字コードをサーブレット・コンテナに伝え、適切なContent-Typeヘッダを出力するには、JSPファイルの先頭にて以下のようなpageディレクティブを記述すればよい。
<%@ page language="java" contentType="text/html; charset=Windows-31J" pageEncoding="Windows-31J" %>
この2つの属性は、それぞれ次のような役目を担っている。
周知のとおり、Java仮想マシン(JVM)の内部では、すべての文字列はUnicodeで表現されている。よってサーブレット・コンテナは、JSPファイルをJVMに読み込むとき、JSPファイルの文字コード(例えばWindows-31J)からUnicodeへの変換を実施する。またWebブラウザにHTML文書を出力するとき、Unicodeから出力先の文字コードへの変換を行う。
contentType属性は、「JSPファイル出力時の文字コード」そして「Content-Typeヘッダに指定する文字コード」の両方を指定するという働きを持つ。上記コード例のように記述すれば、サーブレット・コンテナはコンテンツをWindows-31Jにエンコードして出力する。また同時に、Content-Typeヘッダを通じて文字コード種別をWebブラウザに伝達する。
pageEncoding属性は、「JSPファイル作成時の文字コード」を指定するための属性であり、JSP 1.2仕様(Tomcat 4.0)以降からサポートされている。例えばWindows上で作成したJSPファイルであれば、Windows-31Jを指定しておけばよい。一方、UNIXの日本語エディタで作成したものならば、EUC-JPを指定すれば正しく処理されるだろう。
pageEncoding属性を省略した場合、JSP 1.2仕様では「contentType属性で指定された文字コードでJSPファイルを読み込む」と規定されている。よって通常は省略しても文字化けが発生する恐れはない。とはいえ、Windows-31JからUnicodeへ、UnicodeからWinodws-31Jへという2段階の文字コード変換が実施されていることは認識しておくべきだろう。またインクルードされるJSPファイルではcontentType属性を記述できないため、pageEncoding属性の利用が必須となる。この辺りの問題については、次回説明する予定だ。
ちなみにcontentType属性とpageEncoding属性には、それぞれ異なる文字コードを指定することもできる。例えばシフトJISで作成したJSPファイルをEUC-JPやUTF-8などで出力するといったことも可能だ。
Copyright © ITmedia, Inc. All Rights Reserved.