JSP/サーブレット・プログラミングで誰もが一度は遭遇するトラブルが文字化けだ。予期せぬ文字化け発生に、デバックに苦労した経験を持つ読者も多いだろう。本連載では、JSP/サーブレットにおける文字列の扱いの基礎を復習した上で、文字化けの解決策を要点よく解説してきた。今回が最終回。(編集部)
本記事は2005年に執筆されたものです。Javaの文字化け全般の最新情報は@IT Java Solution全記事一覧のカテゴリ「トラブル・問題解決/ノウハウ/文字化け」をご参照ください。
解答:親ページではcontentType属性を、子ページではpageEncoding属性を指定しましょう
JSPのインクルード機能は文字化けが起こりやすいポイントの1つである。その原因を明らかにする前に、まずは同機能について簡単におさらいしておこう。JSPでは、外部のJSPページやHTMLページをインクルードする手段として、以下の2種類を提供している。
includeディレクティブの場合、インクルードする側のJSPページ(以下、親ページ)のコンパイル時に、インクルードされる側のJSPページやHTMLページ(以下、子ページ)が親ページの一部として挿入される。いわば「静的なインクルード機能」だ。これに対しincludeアクションの場合、Webブラウザが親ページをリクエストするたびに、子ページのコンテンツがレスポンスに挿入される。すなわち「動的なインクルード機能」である。
このいずれの手段を用いる場合でも、文字化けを防ぐ基本ルールは「pageディレクティブのcontentType属性とpageEncoding属性を適切に記述する」ことである。本連載の第1回「Webブラウザが文字コードを判定する基準は?」でも説明したとおり、これら2つの属性はそれぞれ以下の役割を担っている。
周知のとおり、一般的なJSPページではcontentType属性に正しい文字コード名を記述しておけば文字化けは発生しない。なぜならpageEncoding属性は省略可能であり、その場合はContentType属性で指定した文字コードがJSPページの読み込み時にも使用されるからだ。
しかしインクルードされるJSPページでは事情が異なる。上述したとおり、contentType属性の本来の目的は「HTTPレスポンスに用いる文字コードの指定」であるため、親ページ側で1回だけセットすればよく、子ページ側でも重複して記述するのは無意味である(二重指定時の振る舞いは実装依存であるが、Tomcatでは単に無視される)。
とはいえ、子ページからcontentType属性を取り去ってしまうと、今度はサーブレット・コンテナが子ページの文字コードを判別する手立てがなくなり、子ページ部分の文字化けが発生してしまう。そこで子ページには、読み込み時の文字コードを伝える手段としてpageEncoding属性だけを記述しておく。
インクルード時の文字化けを防ぐためのルールをまとめると、以下のようになる。
例えば、それぞれ以下のような内容でpageディレクティブを記述すればよい。
親ページ:parent.jsp
<%@ page language="java" contentType="text/html; charset=Windows-31J" %> <html> <head><title>インクルードのテスト</title></head> <body> これ読めますか:<%@ include file="child.jsp" %><BR> これ読めますか:<jsp:include page="child.jsp" /><BR> </body> </html>
子ページ:child.jsp
<%@ page pageEncoding="Windows-31J" %> こんにちは
図1は、上記コードをTomcat 5.0.28で表示した例である(Tomcat 4.1.31でも動作確認済み)。
解答:属性指定の重複を避けるか、includeアクションを使いましょう
Tomcat 4では、includeディレクティブに限って、「pageディレクティブの各属性を1回しかセットできない」という制限がある。例えばcontentType属性やpageEncoding属性を親ページと子ページの両方に指定し、includeディレクティブで子ページを挿入すると、図2のようなエラーが発生する。
このエラーを回避する方法はいくつかある。
例えば、同じTomcat 4でもincludeアクションを使ったり、includeディレクティブのままでもTomcat 5にアップグレードしたりすることで、この問題を解消できる。もしくは上述したコード例のように、親ページと子ページでそれぞれ異なる属性を使う方法もある。
また最後の「子ページから文字コード指定を外す方法」について補足しておこう。Tomcat 4のincludeディレクティブには「デフォルト・エンコーティングで子ページを読み込む」という固有の振る舞いがある。よって、子ページから文字コード指定をすべて削除し、デフォルト・エンコーティングで読み込ませれば上記エラーを回避できる。ただし、この方法はincludeアクションやTomcat 5では逆に文字化けを引き起こすので注意が必要だ。
解答:HTMLページをデフォルト・エンコーティングで作成するか、JSP化しましょう
インクルードする子ページがJSPではなくHTMLページであると、その文字コードをサーブレット・コンテナに伝える手段がなくなる。そこでTomcat 4およびTomcat 5では、非JSPの子ページをデフォルト・エンコーティングで読み込む仕様になっている。HTMLページをJVMのデフォルト・エンコーティングで作成しておけば、文字化けは起きないはずだ。
もっとも、前回も説明したとおり、デフォルト・エンコーティングへの依存はバグのもとである。例えばWindows環境では問題なく動作していたインクルードが、Linuxサーバ上にデプロイした途端に文字化けする、といったトラブルが起きやすくなる。子ページのデフォルト・エンコーティング依存を避けたいときは、子ページもJSPページで統一し、pageEncoding属性で文字コードを明示すればよい。
またTomcat 5では、includeディレクティブに限って「非JSPの子ページを常にISO-8859-1として読み込む」という制限がある。このケースでは子ページをデフォルト・エンコーティングで作成したとしても文字化けを防げない。よって代わりにincludeアクションを用いるか、子ページをJSP化するなどの対策が必要となる。
Copyright © ITmedia, Inc. All Rights Reserved.