概要
昔からあるWebアプリケーションのHTML文字コード設定をUTF-8に変える時のポイントをまとめてみる。
前提
- Webページの文字コード設定をShift_JISからUTF-8へ変更する
- 利用したソフトウェアは以下
- OpenJDK 11
- WebSphere Liberty 19.0.0.3
- Db2 11.1.4.4
- Chrome
- Java, Liberty, ChromeはmacOS、Db2はコンテナで稼働させる
Db2環境構築
env_list という名前で以下の内容のファイルを作っておく
LICENSE=accept
DB2INSTANCE=db2inst1
DB2INST1_PASSWORD=password
DBNAME=testdb
BLU=false
ENABLE_ORACLE_COMPATIBILITY=false
UPDATEAVAIL=NO
TO_CREATE_SAMPLEDB=false
REPODB=false
IS_OSXFS=false
PERSISTENT_HOME=true
HADR_ENABLED=false
ETCD_ENDPOINT=
ETCD_USERNAME=
ETCD_PASSWORD=
# Docker環境の構築
brew install docker docker-machine
docker-machine create --driver virtualbox default
eval $(docker-machine env)
docker-machine ip
# Db2コンテナの稼働
docker login
docker pull store/ibmcorp/db2_developer_c:11.1.4.4-x86_64
docker run -h db2server --name db2server --detach --privileged=true -p 50000:50000 -p 55000:55000 --env-file env_list store/ibmcorp/db2_developer_c:11.1.4.4-x86_64
docker exec -it db2server bash
# ibm-943コードページのデータベース作成
su - db2inst1
db2 create db ibm943db using codeset ibm-943 territory jp collate using identity
HTMLの変更箇所
- HTML5仕様には文字コードにはUTF-8を指定するべきとの記載あり
- その他のモダンなWebページの書き方はHTML5 BoilerplateやNu HTML Checkerを使うと便利
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>title</title>
</head>
<body>
...
JSPの変更箇所
HTTPレスポンスのcontent-typeとJSPファイル自体の文字コードは以下のように指定する
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
WebページをUTF-8に変えると何が起きるか
ここからが本題。Webページを変更した時にサーバーサイドではどんな対処が必要なのかまとめてみる。
ブラウザからサーバーへ送信されるデータの文字参照が変わる
ユーザーが入力した文字によっては、ブラウザによって数値文字参照へ変換されて送信される。Shift_JISを利用する場合と、UTF-8を利用する場合で数値文字参照へ変換される文字が異なる。例えば、😀という絵文字はWebページがShift_JISの場合、😀
に変換されるが、UTF-8の場合は😀がそのまま送信される。以下の例では、¢、𠮷などで数値文字参照が利用される。
ユーザーの入力 | Unicodeコードポイント | Formエンコードデータ(Shift_JIS)の場合 | Formエンコードデータ(UTF-8)の場合 | 備考 |
---|---|---|---|---|
a | U+0061 | a | a | |
& | U+0026 | &(%26) | &(%26) | |
¢ | U+00A2 |
¢ (%26%23162%3B) |
¢(%C2%A2) | CENT SIGN. Shift_JIS に存在しない文字 |
¢ | U+FFE0 | ¢(%81%91) | ¢(%EF%BF%A0) | FULLWIDTH CENT SIGN |
— | U+2014 |
— (%26%238212%3B) |
—(%E2%80%94) | EM DASH |
― | U+2015 | ―(%81%5C) | ―(%E2%80%95) | HORIZONTAL BAR |
- | U+FF0D | -(%81%7C) | -(%EF%BC%8D) | FULLWIDTH HYPHEN-MINUS |
− | U+2212 | −(%81%7C) | −(%E2%88%92) | MINUS SIGN(上記も%81%7Cとなっていることに注意) |
∥ | U+2225 | ∥(%81a) | ∥(%E2%88%A5) | PARALLEL TO |
‖ | U+2016 |
‖ (%26%238214%3B) |
‖(%E2%80%96) | DOUBLE VERTICAL LINE |
~ | U+FF5E | ~(%81%60) | ~(%EF%BD%9E) | FULLWIDTH TILDE |
〜 | U+301C |
〜 (%26%2312316%3B) |
〜(%E3%80%9C) | WAVE DASH |
¦ | U+FFE4 | ¦(%FAU) | ¦(%EF%BF%A4) | FULLWIDTH BROKEN BAR |
¦ | U+00A6 |
¦ (%26%23166%3B) |
¦(%C2%A6) | BROKEN BAR |
① | U+2460 | ①(%87%40) | (%E2%91%A0) | |
㈱ | U+3231 | ㈱(%87%8A) | ㈱(%E3%88%B1) | |
あ | U+3042 | あ(%82%A0) | あ(%E3%81%82) | |
ア | U+FF71 | ア(%B1) | ア(%EF%BD%B1) | 半角カタカナ |
髙 | U+9AD9 | 髙(%FB%FC) | 髙(%E9%AB%99) | IBM 拡張漢字(JIS 第 1〜4 水準外) |
㐂 | U+3402 |
㐂 (%26%2313314%3B) |
㐂(%E3%90%82) | JIS 第 3 水準 |
彅 | U+5F45 | 彅(%FAg) | 彅(%E5%BD%85) | JIS 第 3 水準 |
繫 | U+7E6B |
繫 (%26%2332363%3B) |
繫(%E7%B9%AB) | JIS 第 3 水準.JIS X 0213 へ 2004 年に追加 |
葛 | U+845B | 葛(%8A%8B) | 葛(%E8%91%9B) | |
葛 󠄀 | U+845B U+E0100 |
葛� (%8A%8B%26%23917760%3B) |
葛+異体字セレクタ(%E8%91%9B%F3%A0%84%80) | 異体字セレクタを利用 |
パ | U+30D1 | パ(%83p) | パ(%E3%83%91) | |
パ | U+30CF U+309A |
パ (%83n%26%2312442%3B) |
ハ+半濁点(%E3%83%8F%E3%82%9A) | 結合文字 |
😀 | U+1F600 |
😀 (%26%23128512%3B) |
😀(%F0%9F%98%80) | サロゲートペア |
𠮷 | U+20BB7 |
𠮷 (%26%23134071%3B) |
𠮷(%F0%A0%AE%B7) | サロゲートペア |
DBとの通信時に変換エラーが発生する
WebページがUTF-8の場合、数値文字参照ではなく文字そのものがブラウザからサーバーへ送信される。サーバーサイドではJDBCドライバーがDBのコードセットに応じて変換を行う。文字によってはSELECTのタイミングで CharConversionException(MalformedInputException) が発生する。
// スタックトレースの最後の部分のみ抜粋
Caused by: com.ibm.db2.jcc.am.SqlException: [jcc][t4][1065][12306][4.25.13] java.io.CharConversionException をキャッチしました。 詳しくは、添付の Throwable を参照してください。 ERRORCODE=-4220, SQLSTATE=null
at com.ibm.db2.jcc.am.b6.a(b6.java:794)
at com.ibm.db2.jcc.am.b6.a(b6.java:66)
// ・・・中略・・・
Caused by: java.nio.charset.MalformedInputException: Input length = 1
at java.nio.charset.CoderResult.throwException(CoderResult.java:274)
at com.ibm.db2.jcc.am.x.a(x.java:52)
at com.ibm.db2.jcc.am.bh.a(bh.java:2952)
... 50 more
-Ddb2.jcc.charsetDecoderEncoder=3
というオプションを付与すると、この例外発生を回避できる。変換エラーが発生した文字は、REPLACEMENT CHARACTER(\uFFFD�)へ自動変換される
- https://www-01.ibm.com/support/docview.wss?uid=swg22005262
- https://www-01.ibm.com/support/docview.wss?uid=swg21973226
なおCENT SIGN(U+00A2)は、-Ddb2.jcc.charsetDecoderEncoder=3
の設定有無に関わらず単純に文字が消える。ある文字がそれぞれの文字コードに含まれているかはこちらのサイトで確認できる(今回の場合はCENT SIGNはデータベースが利用しているcp943には含まれていないことがわかる)
U+00A2
http://www.fileformat.info/info/unicode/char/00a2/charset_support.htm
上記の表の文字(a,&,¢,¢,—,―,-,−,∥,‖,~,〜,¦,¦,①,㈱,あ,ア,髙,㐂,彅,繫,葛,葛󠄀,パ,パ,😀,𠮷)をWebページから入力し、DBへ格納して再度Webページへ表示した結果は以下の通り。
UTF-8を利用する場合、―(U+2015)などの一部の文字で文字化けが発生する。-Ddb2.jcc.ccsid943Mapping=2
をJVMオプションに付与することで変換先を変更できるが、文字化けを完全に解消することはできない。Unicode上の複数のコードポイントをibm-943上では単一のコードに割り当てていることから避けることができない。WebページがShift_JISの場合に文字化けが発生していないように見えるが、前述の通り数値文字参照が利用されているためである。
- https://www-01.ibm.com/support/docview.wss?uid=jpn1J1008522
- https://www-01.ibm.com/support/docview.wss?uid=jpn1J1011940
ユーザーの入力 | Unicodeコードポイント | DBへINSERTしてSELECTした結果(WebページがShift_JISの場合) | DBへINSERTしてSELECTした結果(WebページがUTF-8、-Ddb2.jcc.ccsid943Mapping=2 なしの場合) |
DBへINSERTしてSELECTした結果(WebページがUTF-8、-Ddb2.jcc.ccsid943Mapping=2 ありの場合) |
---|---|---|---|---|
a | U+0061 | a | a | a |
& | U+0026 | & | & | & |
¢ | U+00A2 | 数値文字参照として扱われる | 文字が消える | 文字が消える |
¢ | U+FFE0 | ¢ | ¢ | ¢ |
— | U+2014 | — | — | ― (HORIZONTAL BAR, U+2015)へ化ける |
― | U+2015 | ― | — (EM DASH, U+2014)へ化ける | ― |
- | U+FF0D | - | − (MINUS SIGN, U+2212)へ化ける | - |
− | U+2212 | − | − | - (FULLWIDTH HYPHEN-MINUS, U+FF0D)へ化ける |
∥ | U+2225 | ∥ | ‖ (DOUBLE VERTICAL LINE, U+2016)へ化ける | ∥ |
‖ | U+2016 | ‖ | ‖ | ∥ (PARALLEL TO, U+2225)へ化ける |
~ | U+FF5E | ~ | 〜 (WAVE DASH, U+301C)へ化ける | ~ |
〜 | U+301C | 〜 | 〜 | ~ (FULLWIDTH TILDE, U+FF5E)へ化ける |
¦ | U+FFE4 | ¦ | ¦(BROKEN BAR, U+00A6)へ化ける | ¦ |
¦ | U+00A6 | ¦ | ¦ | ¦ (FULLWIDTH BROKEN BAR, U+FFE4)へ化ける |
① | U+2460 | ① | ① | ① |
㈱ | U+3231 | ㈱ | ㈱ | ㈱ |
あ | U+3042 | あ | あ | あ |
ア | U+FF71 | ア | ア | ア |
髙 | U+9AD9 | 髙 | 髙 | 髙 |
㐂 | U+3402 | 数値文字参照として扱われる | ��(REPLACEMENT CHARACTER, U+FFFD)へ化ける | ��(REPLACEMENT CHARACTER, U+FFFD)へ化ける |
彅 | U+5F45 | 彅 | 彅 | 彅 |
繫 | U+7E6B | 数値文字参照として扱われる | 繋(U+7E4B)へ化ける | 繋(U+7E4B)へ化ける |
葛 | U+845B | 葛 | 葛へ化ける | 葛へ化ける |
葛 󠄀 | U+845B U+E0100 | 葛󠄀(一部が数値文字参照となる) | 葛 �� へ化ける | 葛 �� へ化ける |
パ | U+30D1 | パ | パ | パ |
パ | U+30CF U+309A | パ(一部が数値文字参照となる) | ハ �� へ化ける | ハ �� へ化ける |
😀 | U+1F600 | 数値文字参照として扱われる | �� へ化ける | �� へ化ける |
𠮷 | U+20BB7 | 数値文字参照として扱われる | � へ化ける | � へ化ける |