Webブラウザが文字コードを判定する基準は?Javaの文字化け対策FAQ(1)

JSP/サーブレット・プログラミングで誰もが一度は遭遇するトラブルが文字化けだ。予期せぬ文字化け発生に、デバックに苦労した経験を持つ読者も多いだろう。本連載では、JSP/サーブレットにおける文字列の扱いの基礎を復習した上で、文字化けの解決策を要点よく解説していく予定だ。(編集部)

» 2004年12月25日 00時00分 公開
[吉川和巳スティルハウス]

本記事は2004年に執筆されたものです。Javaの文字化け全般の最新情報は@IT Java Solution全記事一覧のカテゴリ「トラブル・問題解決/ノウハウ/文字化け」をご参照ください。


質問1: Webブラウザが文字コードを判定する基準は何ですか?

 解答:HTTPのContent-Typeヘッダです

 まずは、Webにおける文字コードの扱いをおさらいしておこう。HTML 4.01仕様では、Webブラウザが以下の優先順位で文字コードを決定することを規定している。

  1. HTTPにおけるContent-Typeヘッダのcharsetパラメータ
  2. HTML文書内のMETA宣言およびhttp-equiv属性で設定された、Content-Typeヘッダのcharsetパラメータ
  3. HTML文書内の各要素のcharset属性

 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のように日本語全体が文字化けしてしまう。

図1 pageディレクティブの設定ミスによる文字化け 図1 pageディレクティブの設定ミスによる文字化け

 このように、JSPファイル内のMETA宣言による文字コード指定はうまく働かない。なぜなら、TomcatはContent-Typeヘッダにデフォルトの文字コードであるISO-8859-1を指定するため、META宣言よりもそちらが優先されてしまうのである。

 こうしたトラブルを避けるには、出力するHTML文書の文字コードをTomcatにきちんと伝える必要がある。具体的には、以下の2つの方法がある。

  • JSPの場合:pageディレクティブで文字コードを指定する
  • サーブレットの場合:HttpServletResponse.setContentTypeメソッドで文字コードを指定する

 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利用時は、この行をコメントアウトしておこう。

質問2: pageディレクティブで文字コードを正しく指定する方法は?

 解答:contentType属性とpageEncoding属性を記述します

 JSPファイルの文字コードをサーブレット・コンテナに伝え、適切なContent-Typeヘッダを出力するには、JSPファイルの先頭にて以下のようなpageディレクティブを記述すればよい。

<%@ page language="java" contentType="text/html; charset=Windows-31J"
    pageEncoding="Windows-31J" %>

 この2つの属性は、それぞれ次のような役目を担っている。

  • contentType属性:JSPファイル出力時の文字コード、およびContent-Typeヘッダに出力する文字コード名を指定する
  • pageEncoding属性:JSPファイル作成時の文字コードを指定する

 周知のとおり、Java仮想マシン(JVM)の内部では、すべての文字列はUnicodeで表現されている。よってサーブレット・コンテナは、JSPファイルをJVMに読み込むとき、JSPファイルの文字コード(例えばWindows-31J)からUnicodeへの変換を実施する。またWebブラウザにHTML文書を出力するとき、Unicodeから出力先の文字コードへの変換を行う。

contentType属性の役割

 contentType属性は、「JSPファイル出力時の文字コード」そして「Content-Typeヘッダに指定する文字コード」の両方を指定するという働きを持つ。上記コード例のように記述すれば、サーブレット・コンテナはコンテンツをWindows-31Jにエンコードして出力する。また同時に、Content-Typeヘッダを通じて文字コード種別をWebブラウザに伝達する。

pageEncoding属性の役割

 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.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。