文字コード

  • このページは Unicode(UTF-8) で書かれているため、一部の端末では読めない文字があります。

文字化けが起こる要因

ここでは一般人が「文字化け」と認識するものを列挙。

  • 入力と出力の文字エンコーディングが食い違っている。
    • データは Shift_JIS なのに、EUC として解釈されている。
    • データは UTF-8 なのに、Shift_JIS として解釈されている。
  • 変換先の文字セットに該当の文字がない。
    • Unicode → US-ASCII(ISO 646 IRV) + JIS X0208
    • Windows-31J → Shift_JIS, EUC-JP, ISO-2022-JP
  • 文字コード変換表が微妙に異なる。
    • Cp932 と Unicode の変換表と、Shift_JIS と Unicode の変換表の違い。
  • 文字セット、文字セット間変換表の食い違いによる情報劣化。
    • Shift_JIS → Unicode → EUC など。Shift_JIS → EUC と異なった結果になる。
    • Shift_JIS と EUC-JP とは文字エンコーディングが違うだけで同じ文字セットを使っているが、Unicode とは文字セットが異なる。
  • 似ているが微妙に異なる文字セット
    • US-ASCII と JIS X0201 ラテン文字用図形文字集合
    • Shift_JIS と Windows-31J
  • 本来同じ文字コードと考えられるのに字形が違う。
    • JIS X0208-1978 と JIS X0208-1983 と JIS X0213:2004
    • いわゆる Vista フォント
  • 独自拡張されている。(機種依存文字)
    • Shift_JIS と Windows-31J の違い。
    • NEC拡張文字
    • IBM拡張文字
    • 携帯各社の顔文字など
  • いわゆる外字を使っている。
  • 文字セットに対応するフォントがない。
    • 英語(欧文)の文字セットしかないのに日本語を表示しようとしている。
    • 日本語の文字セットしかないのにハングルや中国語を表示しようとしている。
  • 文字セットとフォントが食い違っている。
    • 日本語の文字セットでハングルや中国語を表示しようとしている。
  • 文字に対応するフォントがない。
    • 1文字だけ豆腐になるなど。
  • 文字コード自動判定の誤判定
  • マルチバイト文字が、内部でただのシングルバイト列と認識されていて、途中で分断されてしまう。(行の折り返しなど)
  • マルチバイト文字の一部が違う意味で認識される。(パス区切り('\','/'など)、エスケープ文字('\' など)、変数(Perl での $変数名 など)、CSV区切りなどなど。)
  • エスケープシーケンスの漏れによるもの。(文字列途中から文字化け)
  • エスケープシーケンスが解釈されていない。
  • エスケープシーケンスによりバイト数が膨れ上がって、サイズオーバー。あるいは切り落とされる。
  • 特殊な変換が必要だが、正しく行われていない。(素通しなど。)
  • 特殊な変換がされているが、受け手で解釈されない。
    • 電子メールの Content-Transfer-Encoding: quoted-printable
    • 電子メール/HTTP での Content-Disposition ヘッダでの日本語ファイル名など (RFC 2231 に従うべき。)
  • 正式な規格にならなかった変換方式で流されている。(Experimental なのに採用されてしまっているなど)
    • 電子メールの unicode-1-1-utf-7
  • 間違った charset が設定されている。
    • charset に SJIS, EUC, none など、一見正しそうで間違っているものが設定されている。
    • 間違っていても自動判別で正しく動いてしまうので気が付きにくい。
  • 一部の(過去の)独自実装で流されている。
    • HTML での charset の x-sjis, x-euc-jp 指定など。
  • 間違った文字セットが送られてきている。
    • Shift_JIS,EUC-JP,ISO-2022-JP なのに、文字セットに Windows-31J が使われているケースがある。(単純な文字エンコーディング変換を行った結果発生する。)
  • ビット落ちしている。
    • 8bit の文字なのに、7bit で解釈されている。
  • 「未定義」領域の文字コードが、代替文字に変えられたり、消されたりしている。
  • 「改行」コードが異なる。
  • 改行コードが変換されて、改行が余計にくっついてしまう。
    • 二重に改行される。
    • CR+LF が CR+CR+LF になる。
    • CR+LF が CR+LF+CR+LF になる。
  • 改行コードが変換されない。
    • 改行されずにつながってしまう。
    • LF のままで Windows/MS-DOS で読む。
    • 行末に"^M"が付く。
    • CR+LF のままで Unix で読む。
  • 画像などのバイナリデータで 0x0d 0x0a の連続するデータをうっかりテキストモードで読み込むと Windows では、0x0a に化ける場合がある。(要注意)
  • 画像などのバイナリデータで 0x0a を含むデータをうっかりテキストモードで書き出すと Windows では、0x0d 0x0a に化ける場合がある。(要注意)
  • Unicode でサロゲートペアが正しく処理されていない。
    • サロゲートペアの範囲のフォントがない。
    • サロゲートペアが1文字として取り扱われていない。
      • サロゲートペアの途中で分割
      • 2文字に見えている。(□□に化けるなど。)
      • 文字数の数え間違え。
    • サロゲートペアのまま UTF-8 に変換している。(違反なので、正しく取り扱われない可能性がある。)

文字化けに対する対処

  • 文字エンコーディングをシステム全体で統一する。
    • 今なら Unicode がお勧め。次点は EUC。Windows で統一するなら Shift_JIS もあり。
    • Shift_JIS(EUC-JP, ISO-2022-JP)で出力するなら、Shift_JIS で入力するのが正しい。
    • Windows-31J で入力するなら、出力は Windows-31J か Unicode で行う必要がある。(上方変換)
      • 入力は Windows-31J で出力は Shift_JIS, ISO-2022-JP, EUC-JP というのはダメ。(下方変換)
      • 下方変換による文字化けは減らしようがない。よって、最初から使えなくするか、文字化けを我慢するか、代替文字に置き換えるかしかない。
  • 文字エンコーディング変換を極力減らす。(そのままスルーがベスト。ただし、インジェクションに注意。)
    • 特にコード体系がまったく異なる Unicode と JIS X0208,X0212 との間の変換がなくなるようにする。
    • ISO-2022-JP, EUC-JP, Shift_JIS(Windows-31J) との場合、同じ ASCII 領域と JIS X0208 領域を使っている部分には互換性がある。それ以外は互換性がない。(エンコードできずに文字が欠ける可能性。)
  • 数値文字参照(♥とか)や文字実体参照(♥とか)の安易な利用はダメ!
    • HTML/XML ブラウザにしか通用しないので、それ以外の部分に持っていくと生の実体参照文字列が出てしまう場合がある。(HTML に限定するのであればアリ。)
    • HTML/XML の入力時に Unicode 化、出力時に数値文字参照化が多分正しい。
    • サイズに限界がある場合、数値文字参照のままDBに保存はやめた方が吉。(大幅にバイトサイズが大きくなるので、サイズに制限があると厳しいこともある。)
      • しかし、U+10000〜の処遇はどうするかね。
  • どこで文字エンコーディングが変換されるかを明確にする。
    • 文字コードの流れを明確にする。
  • 文字エンコーディング変換を局所化する。
    • 行き当たりばったりにあちこちで変換しない。
    • 入力部、出力部のみで変換をかけるのがベスト。
  • 「未定義」領域の場合、入力段でのみチェックを行い、内部的にはそのまま通す。
    • 規格が変わって未定義領域に文字が追加されることがある。真面目に規格通りでハードコーディングしてあると、規格に追従できなくなる。
    • 最終的な表示はレンダラの仕事。それまでは単なる「通信路」でしかないと心得る。「通信路」は無闇に検閲しないのが原則。
  • 文字エンコーディング変換ルールを明確にする。
    • 「変換機能があるからそれ使えばいい」というのはアウト!
    • 変換表が妥当かどうかの判断が必要。特に Unicode との変換はかなり癖があるので要注意。
    • 変換表が妥当でなければ、何らかの対策が必要。
  • 文字エンコーディング判別ルールを明確にする。
    • 「自動判別機能があるからいい」というのはアウト!
    • どういう判別を行っているのかを正しく理解する。(統計? 特定の文字の出現?)
  • 使えない文字を明確にする。使えない文字があった場合、エラーにするなどの対処をする。
  • 文字コードを指定する所では正しく指定する。(charset 指定)
  • 特定の文字エンコーディングにしか現れない文字をデータの手前側に配置する。(自動判別対策)
  • 同じと考えられる文字はどちらか片方にする。(JIS X0201 の "1" と JIS X0208 の "1" をごちゃ混ぜに取り扱わない。)
  • わざとダウンコンバージョンする。
    • そのまま変換しようとしても「完全な同字」がないため豆腐になってしまう場合、同字と考えられる文字に合わせる。字形は化けるが、豆腐になるよりまし。(髙(はしご高)→高 など)
  • 可能であれば、文字セット/フォントセット/言語がわかる仕組みを別に用意する。(文字コードだけだとこれらを含まないので、たとえば Unicode だと字形の揺れが出る。)
  • JIS X0201 片仮名は極力避ける。
  • 文字に幅があるとか、全角/半角という概念は捨てる。バイト数=文字幅という概念は捨てる。
  • チェックの時には「ラウンドトリップ」のおかげで一見正しく見えてしまうことがあるのを忘れない。他のシステムからも確認の必要がある。
  • 生データ(バイナリ、16進ダンプ)を確認すること。一度変換された文字コード(あるいはさらに再変換された文字コード)をいくら眺めていても、原因はわからない。
    • 化けた文字を端末やエディタでさらに化けさせてどうするのか?
  • サーバーの実行環境の文字コード/ロケール、クライアントの実行環境の文字コード/ロケールの違いがあることに注意せよ。
    • サーバーが勝手にクライアントに合わせてくれるわけではないし、クライアントが勝手にサーバーに合わせてくれるわけでもない。
  • 完璧に文字化けをなくすことは不可能。実用でどこまで許容するのかを決める。

いわゆる半角カナをどう扱うか?

  • メールでよく使われる ISO-2022-JP では半角カナは扱えない。
  • 検索などでは激しく邪魔。
  • 将来半角カナはなくなる方向に向かっている。
  • 最大の問題は、Shift_JIS と EUC-JP との混同。Shift_JIS の半角カナは EUC-JP の漢字と区別がつかない。

入力されたらエラーとする。

  • ユーザーからの不満が高い。

いわゆる全角カナに変換する。

  • たぶんこちらの方が望ましい。

Windows-31J をどう扱うか?

問題

Windows の Internet Explorer などでは、 charset に Shift_JIS, EUC-JP, ISO-2022-JP と指定してあっても、 平気で Windows-31J の文字セットを送ってくる。

Windows-31J で送ってきたかどうかの判別はできない。(「未定義」領域から送信されたかのように見える。)

そのまま素通しする

  • 化けるかもしれないが、気にしない。(それほど文字化けにセンシティブではない場合には選択可能。)
  • 少なくとも流した人間はそのまま読む事ができる。(ラウンドトリップ現象)
    • 他の印字(あるいは検索)システムに持ち込まなければ問題はない。

Windows-31J だと仮定して取り込む

  • 世の中のクライアントの 80% 以上が Windows という現状を考えると、「デファクト」と認めるしかないのかも?
  • システムが Unicode であれば、Unicode へ取り込み、Unicode で表示は可能。
    • この場合、Unicode で表示できないのは端末側の問題。
    • Unicode 未対応の端末を見捨てることになる。
  • 実は Windows-31J ではない別の文字セットだった場合に化ける。(Macとか…)

ASCII + 正式な JIS X0208 の範囲だけを受け取るようにする

  • はじいた後の文字列を表示して、入力者に確認してもらうのが望ましい。
  • ユーザーから見ると、一部の文字(丸数字、ローマ数字、記号、漢字)が入力不可に見える。
  • 受け取った後の化けは発生しない。
  • 限定の仕方を間違えると「日本語しか通さない」システムになる。(日本国内のみで使うなら問題はない。)
    • Shift_JIS, EUC, ISO-2022 からの受け取り時だけではじくこと。Unicode になってからはじくのは問題が多い。
  • Unicode になってしまった後で判定するなら、Java の場合、Unicode→ISO-2022-JP→Unicode と変換して確認することができる。(半角カナも退治。)
    • なんか半角カナが通ってる。(0x1b 0x28 0x49 JIS X0201 片仮名集合呼び出し)

がんばって代替文字(列)に置き換える

  • ローマ数字のⅡを英数字のIIにするとか、"(株)"とか。
  • 漢字や一部の記号は代替文字列が見つからない。
  • 実装に苦労する割に報われない可能性大。

その他細かな対処

character sets (文字セット)

Unicode への変換

  • 0x00〜0x7f は変換せずそのままにする。(日本で円記号とされる"\"(0x5c)や、オーバーラインとされる"~"(0x7e)も!)
  • それ以外の文字を 0x00〜0x7f に変換しない。

Unicode からの変換

  • 0x00〜0x7f は変換せずそのままにする。(日本で円記号とされる"\"(0x5c)や、オーバーラインとされる"~"(0x7e)も!)
  • それ以外の文字を 0x00〜0x7f に変換しない。

HTTP

  • HTTPヘッダで、Content-Type: ... charset= が正しい charset を出力しているか確認する。
    • 一時期、Apache の httpd.conf (設定ファイル)の DefaultCharset が ISO-8859-1 だった…
    • 一時期、Tomcat が勝手に ISO-8859-1 を付けていた…
  • HTTPヘッダが使えない場合、HTML の meta タグを使う。HTTPヘッダで charset が設定されていないことが条件。
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS" />
    ...
    </head>
    • オフラインでは HTTP ヘッダはないので、http-equiv は必ず付けておくべき。
    • charset は、title 要素(あるいはコメントも?)などで ASCII 以外の文字が現れる前に書かなければならない。(そうしないと自動判定行きになり、判定に失敗すると化ける。)
  • meta タグの解釈をしてくれなさそうな場合、そのエンコーディングでしか現れない文字を入れて、自動判別に任せる。(いわゆる「美乳テーブル」の文字を入れておく)

XHTML

  • IE6 だけ、DOCTYPE 前に何かの文字があると互換モードになってしまう。なので、XML宣言を置くことができない。
  • IE7 では、言語指定は html 要素の lang 属性でしかできない。xml:lang は無視される。
    • UTF-8で書いてある場合で、言語が不明の場合、英語版IE7では中国語フォントで表示されることになる。
      • 無理やり日本語フォントで表示したいなら、Tools→Internet Options→General→Fonts で Language script: Latin based を日本語フォント(MS PGothic とか)にする。
  • IE6, IE7 ともに、HTTP で Content-Type: application/xhtml+xml を解釈できず、ダウンロードファイルとして扱われてしまう。なので、HTTP の Content-Type は text/html にする必要がある。

HTMLフォーム

  • 過去のしがらみがないのなら、form 要素に enctype="multipart/form-data" を指定する。正しいブラウザであれば、MIME ヘッダに Content-Type として charset を付けてくれる。
  • enctype を指定しないと送信データの Content-Type は application/x-www-form-urlencoded になってしまう。この場合、クライアントはどの文字コードで送ったのかは教えてくれない。要するにもらえるのは「ただのバイナリデータ列」。
    • charset を付けてくるブラウザがあるが、application/x-www-form-urlencoded の仕様にはそういう定義がない。
  • さらに、表示した画面の文字コードと送られてくる文字コードとの因果関係がない場合がある。
  • GET/POST データを化けさせないために、hidden フィールドに特定の文字(いわゆる「美乳テーブル」の文字)を入れる方法がある。
    <input type="hidden" name="decision_char" value="美">
    • この文字がどんなバイナリで返ってきたかで判定する。(ラウンドトリップではないところがミソ)
      バイナリ文字コード
      E7 BE 8EUTF-8
      7F 8EUCS-2(来るか?)
      1B 24 40 48 7E 1B 28 42ISO-2022-JP (JIS X0208-1978)
      1B 24 42 48 7E 1B 28 42ISO-2022-JP (JIS X0208-1983)
      1B 24 28 4F 48 7E 1B 28 42ISO-2022-JP-3 (JIS X0208:2000)(来るか?)
      94 FCShift_JIS
      C8 FEEUC-JP
      DA B8EUC-KR(参考)
      C3 C0EUC-GB(参考)
    • この項目を入れたら他の項目の文字化けがなくなるわけではない。たいてい項目単位で文字コードの自動判別をしているので、それを止めて、この項目だけで判定して、一律同じ文字コードで変換する。
    • ただし、この場合、国際化では問題になるかも?
  • User-Agent で判断するのは、あまりよくない。
  • 数値文字参照(&#9829;とか)で、GET/POST するブラウザがある。
    • そもそもコード体系にない文字を送ろうとして数値文字参照で送りつけるのだから迷惑千万。
      • Shift_JIS のページなのに Unicode の文字を送りつけられても対処できない。
    • 入力チェックで数値文字参照を見つけたらはじいてしまうのが簡単。
    • Java なら Commons Lang StringEscapeUtils で unescapeHTML すると吉。

Java

  • Java2SE 1.4 までは Java の内部文字コードは UCS-2 である点に注意。(UCS-2(BMP)に含まれない文字は使えない。)
  • Java2SE 5 からは Java の内部文字コードは UTF-16 である点に注意。(サロゲートペアあり。)
  • Sun のリファレンス実装で定義されている「日本語」関連の文字エンコーディングは以下の通り。(ドキュメントから抜粋。IANAでのcharset名と食い違っている点に注意。IANAのcharset名の別名もあるが記載されていない。)
    • Unicode 系
      UTF-88 ビット UCS Transformation Format
      UTF88 ビット Unicode Transformation Format
      UTF-1616 ビット UCS Transformation Format、オプションのバイト順マークによって識別されるバイト順
      UTF-16BE16 ビット UCS Transformation Format、ビッグエンディアンバイト順
      UnicodeBig16 ビット Unicode Transformation Format、ビッグエンディアンバイト順、バイト順マーク付き
      UnicodeBigUnmarked16 ビット Unicode Transformation Format、ビッグエンディアンバイト順
      UTF-16LE16 ビット UCS Transformation Format、リトルエンディアンバイト順
      UnicodeLittle16 ビット Unicode Transformation Format、リトルエンディアンバイト順、バイト順マーク付き
      UnicodeLittleUnmarked16 ビット Unicode Transformation Format、リトルエンディアンバイト順
  • ISO 2022 系
    ISO2022JPJIS X 0201、ISO 2022 形式の 0208、日本語
    JIS0201JIS X 0201、日本語
    JIS0208JIS X 0208、日本語
    JIS0212JIS X 0212、日本語
  • EUC-JP 系
    EUC_JPJIS X 0201、0208、0212、EUC エンコーディング、日本語
    Cp33722IBM-eucJP - 日本語 (5050 のスーパーセット)
  • Shift_JIS 系
    SJISShift-JIS、日本語 (Shift_JIS)
    MS932Windows 日本語 (Windows-31J)
    Cp942IBM OS/2 日本語、Cp932 のスーパーセット
    Cp942CCp942 の拡張
    Cp943IBM OS/2 日本語、Cp932 および Shift-JIS のスーパーセット
    Cp943CCp943 の拡張
  • EBCDIC 系
    Cp930UDC 4370 文字を含む日本語カタカナ漢字、5026 のスーパーセット
    Cp939UDC 4370 文字を含む日本語ラテン文字漢字、5035 のスーパーセット
  • 自動判別
    JISAutoDetectShift-JIS、EUC-JP、ISO 2022 JP の検出および変換 (Unicode への変換のみ)
    注:UTF-8 は判別されない。また、Sun Java リファレンス実装の では Shift_JIS, EUC-JP のサブセット判定をローカルOS名で行っているので危険。(→日記/2006-08-07)

Java JISAutoDetect の詳細

  • ASCII はそのままスルー。それ以外のもの(ESC か 0x80以上)が出るまで読み出す。
  • ISO-2022-JP でデコードしてみて、エラーでなければ、ISO-2022-JP として扱う。(ESC があれば、ESC シーケンスが正しくなくても通る?)
  • ホストOSによって、Shift_JIS, EUC-JP のバリエーションを変える。(要注意!)
  • Shift_JIS と EUC-JP とでデコードしてみて、エラーのない方を採用する。
  • 両方エラーがない場合、より多く入力を読み取った方を採用する。
  • 入力を読み取った長さで区別が付かない場合
    • まったく読み出せていない場合(アンダーフロー?)、アンダーフローにして、追加データをもらう。(バッファがパンクする可能性はないか?)
    • EUC-JP で変換した方にひらがな2文字か半角カタカナ2文字を見つけたら、EUC-JP とみなす。
  • 入力バッファアンダーフロー対策がない。中途半端な場所でアンダーフローが出るとたぶん誤判定する。

Java Servlet

  • フォームから受け取る文字列の文字エンコーディングの設定は HTTPServletRequest#setCharacterEncoding()
  • 出力するページの文字エンコーディングの設定は HTTPServletResponse#setCharacterEncoding()

JSP

  • Java Servlet と同じ注意が必要。
  • JSPコンテナ(JSPコンパイラ)に対して、このページ(ソースファイル)の文字エンコーディングを教える必要がある。(デフォルトは ISO-8859-1)
    • 「JSPソース内」に日本語をまったく含まないのなら不要。ISO-8859-1 扱いでよい。
<%@ page pageEncoding="UTF-8" %>
  • ブラウザがこのページをどう解釈するべきかを指定する必要がある。
    • ページ出力に日本語を含む場合は設定が必要。(常に設定が必要とみるべき。)
<%@ page contentType="text/html; charset=UTF-8" %>

Tomcat

  • URL(URI)を UTF-8 として解釈するようにする。(Tomcat 4.xまで)
    • server.xml の Connector タグに URIEncoding="UTF-8" を設定する。
      <Connector port="8080" URIEncoding="UTF-8" />
  • URL(URI)で受け取ったパラメータに対して、request.setCharacterEncoding(encoding) が適用されるようにする。(危険。今はやるべきではない。)
    • server.xml の Connector タグに useBodyEncodingForURI="true" を設定する。
      <Connector ... useBodyEncodingForURI="true" ... />
    • Tomcat 5.x からは URI はデフォルトで UTF-8 とみなされるようになり、 setCharacterEncoding は適用されなくなった。(URLの解釈としては本来はこれが正しい。)

URL(URI)

  • UTF-8 でエンコードするのが正しい。
    • Shift_JIS や EUC でエンコードされているのは正しくない。
    • POST/GET を同一視する場合に文字エンコーディングが食い違う可能性がある!

JavaScript

  • URI の GET パラメータを指定する場合には、encodeURIComponent() をパラメータごとに使う。
    • もちろん、URI だと筒抜けになりやすいので、GET よりは POST を使う方が望ましい。
  • escape(), encodeURI() の使い方には要注意。
  • escape() は互換性が全くないので、変換したものをブラウザ外に持ち出してはならない。
  • encodeURI() は、URI全体に対してかけるのが正しいが、そもそもクエリ文字列部を後からエンコードすること自体正しくない(たとえば"&"の取り扱いを考えよ)ので、使うべきではない。

メール

  • メールのヘッダで、Content-Type: ... charset= が正しい charset を出力しているか確認する。
    • メールの場合、日本では一般に ISO-2022-JP が使われる。
    • ASCII と ISO-2022-JP 以外を認識できないメーラがある点に注意。(UTF-8 で送られても読めない。UTF-8 だから国際化対応できてますとかいう馬鹿に要注意。)
  • 8bit コードを通さないのであれば、ヘッダに Content-Transfer-Encoding: 7bit を指定する。
    • 一部のメーラ(MUA)では Content-Transfer-Encoding: quoted-printable を解釈できない… (メーラの間違った実装が悪いが、現実に存在する以上、対応せざるを得ない。)
  • ヘッダ内で ASCII 以外の文字コードを使う場合は MIME の Q-encoding か B-encoding を使う。(Q-encoding はごく一部が ASCII 以外の文字の場合に。B-encoding は大半が ASCII 以外の文字の場合に使う。)
  • 添付ファイル名などに使う「ヘッダのパラメータ」には Q-encoding, B-encoding を使ってはならない。RFC2231方式でエンコードする。(どうして、Q-encoding, B-encoding を使わないのか理解できないが、そういう仕様だそうで。 →http://www003.upp.so-net.ne.jp/hat/imail/sec08.html#rfc2231http://www.emaillab.org/essay/japanese-filename.html )
msg.setText(message, "ISO-2022-JP"); 
msg.setHeader("Content-Transfer-Encoding", "7bit");

携帯のメール

PHP

  • PHP の内部文字コードは「8bit文字列」であって「EUC-JP」ではない。(何が入っているのかはプログラマが管理しなければならない。)
  • php.ini の mbstring の設定とその説明がかなりひどいので要注意。
    • 参考 PHPの文字化けを本気で解決する http://hain.jp/index.php/tech-j/2007/02/13/%EF%BC%B0%EF%BC%A8%EF%BC%B0%E3%81%AE%E6%96%87%E5%AD%97%E5%8C%96%E3%81%91
    • default_encoding は Content-Type の charset の出力のこと。
    • mbstring.language は実は mb_send_mail の時だけ使われる謎パラメータ。
    • mbstring.internal_encoding は mbstring の「変換元」デフォルトエンコードを示す。
    • mbstring.http_input, mbstring.http_output は pass にしておく。ちゃんと自分で何を入出力するのか把握すること。
      • http_input はともかく、http_output はひどい仕様だ…これは間違いなく勘違いする。
    • このあたりをちゃんと指定していないと起動タイミングでめちゃくちゃになる。
  • magic_quotes_gpc は絶対に off にすること。on で ShiftJIS を扱うと0x5c(\)問題が起きる。off でまともに動かないアプリはセキュリティホールありだから使うな。

XML

  • エンコーディングをXML宣言で指定する。
<?xml version='1.0' encoding='Shift_JIS' ?>

Windows

  • 外国産のソフトは、各種コントロールに欧文フォントが固定で埋め込まれてしまっていて、日本語を表示させると化けてしまう場合がある。

Linux

  • /etc/sysconfig/i18n に定義がある。
    • 最近は LANG="ja_JP.UTF-8" になっているはず。

Unix シェル

  • 環境変数 LANG に正しい文字コードが設定されているか確認する。
  • どうしてもダメな場合 LANG=C を設定する。

フォント

メモ

US-ASCII と JIS X0201 ラテン文字用図形文字集合

US-ASCII →(参考)→ ISO 646 →(ローカライズ実装)→ JIS X0201 ラテン文字用図形文字集合

文字コードASCIIJIS X0201 ラテン文字用図形文字集合
0x5cREVERSE SOLIDUSYEN SIGN
0x7eTILDEOVERLINE
  • バックスラッシュ(REVERSE SOLIDUS(逆斜線))と円記号(YEN SIGN)の違いはよく知られている。
    • MS-DOS のパス区切りは本来バックスラッシュ。
    • C言語などの文字列のエスケープ文字は本来バックスラッシュ。
  • チルダ(TILDE)(波線)とオーバーライン(OVERLINE)(上線)の違い。
  • とっても細かいことを言うと、文字コードは JIS X0201 ラテン文字用図形文字集合をG0集合に呼び出した場合の値。

ISO 8859-1

  • 8bit コードの代表。
  • Latin-1
  • 他にも 8859-* というのがある。
  • 生データが ISO 8859-1 扱いとなっているケースが多い。(8bitの生データとみなすのに都合がよい。)
  • わざと文字エンコーディングを ISO 8859-1 と設定して、生データをとりだすのによい。

EBCDIC

  • いまやメインフレームでしか扱われない。
  • 過去資産のためにやむを得ず使うケースがある。
  • バリエーションが大量にあるので要注意。EBCDIC だから同じと考えてはダメ。
  • 固定長で文字列処理している困ったプログラム多数なので日本語混ぜるときには要注意。
    • SI/SOで文字数とバイト数が狂うケース多々あり。

Java でのラウンドトリップ問題

Java/文字コード#w7279771

JavaMail

  • 制御コード(0x00〜0x1f)が入る場合
    • JavaMail 1.2 はデフォルトで Content-Transfer-Encoding: 7bit
    • JavaMail 1.3 以降はデフォルトで Content-Transfer-Encoding: quoted-printable

MySQL

  • 4.1.x で、クライアント(Shift_JIS)→内部(Unicode)→DB(EUC) のような変換をして文字化け。

Tomcat

  • Tomcat 4.1.29, 〜5.0.28 でHTTPヘッダ(Content-Type の charset)に勝手に ISO-8859-1 をつけていた。

Apache

  • 一時期 Apache 2 の設定ファイル httpd.conf が DefaultCharset に ISO-8859-1 を指定していた。
    • 勝手に付けさせないためには DefaultCharset の行をコメントアウトするのが正しい。

CGI と改行コード

  • 一般に、スクリプトなどで出力される改行コードはホストの改行コードになる。
  • CGI を受け取る Web サーバーもそれを期待していて、改行を変換してくれる。
    • hton(), ntoh() みたいな感じ。内部ではホストのままで取り扱うのが正解。
    • ヒアドキュメントのことを考えれば、これがとても妥当な仕様。
    • うっかり "\r\n" と書くと余計なお世話になる場合が多い。
      • ただし、自分で直接 HTTP プロトコルを話している場合は "\r\n" が必要。
    • Content-Type: text/*, application/xhtml+xml などのテキスト以外の場合、たとえば image/png などは生データがやり取りされる。
  • とりあえず Perl で
    print "\n";
    • Unix では 0x0a。(LF)
    • Windows では 0x0d 0x0a。(CR+LF)

Windows

  • (MS-DOS?), Windows 9x は、内部は Windows-31J。
    • ファイルも Windows-31J
  • Windows NT, 2000, XP は、内部は UCS-2。(UTF-16という話もあるが…)
    • ファイルは Windows-31J だったり、UTF-8 だったり。
  • JIS X0208:2004 に従い、Vista フォントで、表外漢字の正字化をした。(また字形変更… ('A`) )
  • MS-IME で「人名/地名」を選択すると「髙(はしご高)」が選べてしまう模様。(1字のみの人名化けはこいつが原因の可能性が高い。)

HTML 4.01

  • 数値文字参照は ISO 10646 の文字コードであることが保証されている。(5.3.1 数値文字参照 )

HTMLフォーム (W3C HTML4.01 仕様書)

  • GET の場合、UTF-8 でフォームデータを返すのが正しい。(URIの仕様)
    • これはまだ従っていないブラウザが大半。
  • POST の場合
    • http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#adef-accept-charset
    • サーバーからの form タグの accept-charset 属性の中からいずれかの文字コードでエンコードするのが正しい。
    • accept-charset 属性が不明の場合は、送られてきた文字エンコーディングでフォームデータを返してもよい。(MAY)
      • Shift_JIS で表示した場合、Shift_JIS でフォームデータを返すとか。
      • ISO 8859-1 で表示された場合、漢字を入れても ISO 8859-1 では表現できないとみなされて、ブラウザにより "?" や数値文字参照などに置き換えられることがある。
      • 一部のブラウザは送られてきた文字エンコーディングと異なる文字エンコーディングでフォームデータを返す。(受信は EUC-JP 対応だが、送信は Shift_JIS 固定など。) よって、表示が文字化けしないからと言って、フォーム送信が文字化けしない保証はまったくない。
    • URL Encoding (application/x-www-form-urlencoded) 自体はコードを "%nn" で表現するというルールであって、UTF-8 かどうかとはまったく無関係。
      • しかし、どうも勘違いして、UTF-8 を返すべきだと考えている人もいる模様。

Webブラウザ

  • 一部の(古い)ブラウザは、新しい文字エンコーディングを解釈しない。
    • UTF-8 非対応はそれなりにある。
  • 常識的に考えれば、ページの文字エンコーディングで POST するのが正しい。(HTML4.01 では「返すことができる」という表現。)
    • 一部のブラウザは、特定の文字エンコーディングのみで POST する。
  • form タグの accept-charset 属性を指定しても無視するブラウザがある。
  • URL でフォームを返す場合(GET方式)、ページの文字エンコーディングに関わらず、UTF-8で送ってくる場合がある。
    • 一部のブラウザは、特定の文字エンコーディングのみで GET する。

チルダ問題

  • URLなどで、半角チルダ("~" 0x7e)が全角チルダ("~" U+FF5E相当)に変換される
  • Mac OS X, Safari など
  • 原因は Shift_JIS などと Unicode とのベンダ変換表による問題
    • JIS X 0201 にはチルダがない。仕方ないので JIS X 0208 のチルダに変換という流れ。
  • URL エンコーディングしなければならない文字なのかどうかが絡んでややこしくなっていた模様。

ファイルシステム、および、ファイル圧縮、アーカイバソフト

Unicode と他の文字セットとの変換の問題点

  • 本来は 文字セットX → Unicode → 文字セットX が保証されるはず。
  • 現実には変換表が各社間でずれていて保証されない。
    • 文字セットXa →(A社変換表)→ Unicode →(B社変換表)→ 文字セットXb で Xa≠Xb となる。
    • 各社間の字体の認識の違いがある。
    • 1:1 マッピングであるはずが、n:1, 1:n のところがある。情報欠落の可能性がある。
  • Shift_JIS と Windows-31J との違いで、拡張部分が「未定義」扱いされることがある。

改行コード

  • 文字セットの話とは別に改行コードが各所で異なっている問題がある。
    • Unix で書いた文書を Windows で見ると、1行につながって見える。
    • Windows で書いた文書を Unix で見ると、行末に "^M" が付いて見える。(これが原因で誤動作を起こすことがある。)
  • プロトコルによって定義されていることがあるので要注意。
    • 一般に通信プロトコルでは CR+LF が全盛。(昔の名残?)
MS-DOS,WindowsCR+LF
Unix, Mac OS XLF
Mac(OS X 以前)CR
メインフレームNEL
  • CR のみの Mac は異端。
  • C言語, Perl の場合、テキストモードとバイナリモードがあり、テキストモードでは CR+LF と LF とを同一視する場合がある。
    • 外部 CR+LF(or LF), 内部 LF のような使い方をしている。

テキストモードとバイナリモード

  • 何も指定しないとテキストモードになるのが一般的。(テキスト処理が一般的な処理だから。)
  • Unix の人たちは、内部 LF、外部 LF なので、テキストモードとバイナリモードの区別が付かない。なので無頓着。
  • Windows の人たちは、内部 LF、外部 CR+LF なので、テキストモードとバイナリモードを間違えるとデータが化ける。
    • C言語などは元々 Unix からのもので、ソースの互換性を維持するには、内部 LF にしなければならなかった。(移植のために \n を \r\n に書き換えてたら大変。)
  • Unix で作ったアプリケーションを Windows に持ってくると、テキストモードのままでバイナリデータを取り扱ってしまってデータが化ける。
  • CR+LR or LF →(内部へ変換)→ LF →(外部へ変換)→ CR+LF
    • 入力部が CR+LF or LF であるところが曲者。
    • * LF だったものが * CR LF になってしまう。
    • 「CR+LF → LF → CR+LF なんだから、テキストでもバイナリでも結果は同じ」という嘘解説がある。だまされるなかれ。

Windows Vista 関連

  • 特番:Windows Vistaの新文字セットが引き起こすトラブル http://itpro.nikkeibp.co.jp/99/vista/index.html
    • 字形変更により、XP以前の文書をVistaに持ち込むと化ける。(微妙な字形の違いになる。)
    • 字形変更により、Vistaの文書をXP以前に持ち込むと化ける。(微妙な字形の違いになる。)
    • 追加された字があるため、文書をXP以前のマシンに持ち込むと化ける。(読めなくなる。)
    • 一部の漢字がサロゲートペア領域を使用しているため、サロゲートペアを正しく扱っていないアプリで化ける。あるいは入力そのものが不可。
    • Shift_JIS や EUC-JP などがそもそも持たない字を Unicode が持っているために化ける。(これは昔から。)

Unix (iconv, Samba 関連)

文字化けのパターン

  • 片仮名が大量に現れる
    • EUC-JP が Shift_JIS で解釈されている。
    • EUC-JP が JIS X0201 片仮名と間違って解釈されている。
  • ^[$B や ^[(B (あるいは ^[(J )とかが付いて、その後に ASCII 文字が続く
    • ISO-2022-JP が解釈されていない。
  • "?" が大量に現れる。
    • 代替文字に変換された。
  • 0xfffd(\ufffd,\x{fffd}) が大量に現れる。
    • Unicode(UCS-2)の代替文字。Unicodeへの変換に失敗している。

用語

ややこしくなるのでここでの定義。他で通用するかどうかは知らない。

  • 文字セット
    • 文字の集合
  • 文字セット内位置
    • JIS X0208 で言う区点位置
  • 文字エンコーディング
    • 文字セット内位置から文字コードへの変換ルール
      • ShiftJIS, EUC など
    • 「文字セット内位置 = 文字コード」となっているケースもある。(ASCIIなど)
  • 文字コード
    • オクテット列(バイト列)になった文字
  • 字体
    • 文字の大まかな形。文字のデザイン(明朝体とゴシック体)とは無関係。
    • 「同じ文字」か「違う文字」かの基準点
  • 字形
    • 文字の形。デザインにより、揺れがある。
    • 「同じ文字」でもデザインが異なると若干形が異なる。
  • フォント
    • 字形にさらにデザインを持たせたもの。
    • 明朝体やゴシック体など。
  • オクテット
    • 1オクテット=8ビット (厳密)
    • 1バイト≠8ビット (8ビットが一般的だが、それ以外のものもある。)
    • 誤解が生じにくいところでは「バイト」と表現する。

コードの表記について

  • 上位4ビットn, 下位4ビットm の場合、"n/m" (n,m は10進数)と表わすのが正式。
    • ESC は 1/11, "Z" は 5/10
  • しかし、現実にプログラムを作るときには16進数を使うのが一般的なので、0xnm (n,m は16進数) として表す。
    • ESC は 0x1b, "Z" は 0x5a

リンク

書籍

#amazon(4320121023, left) #amazon(4899770510, left) #amazon(4873111080, left) #amazon(4798100307, left) #amazon(4320029046, left)

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2012-09-19 (水) 15:08:01 (1854d)