注目イベント!
春の新人向け連載2025開催中!
今年も春の新人向け連載が始動しました!!
現場で役立つ考え方やTipsを丁寧に解説、今日から学びのペースを整えよう。
詳細はこちらから!
event banner

文字コード これだけは覚えておこう ~UTF-8編~

日本語|English|中国语
| 4 min read
Author: yoshifumi-moriya yoshifumi-moriyaの画像

はじめに

#

今回は新人さんに向けての記事ということで、我々日本語を扱うプログラマーが長年捕らわれ続けている問題である「文字コード」の問題について語りたいと思います。
現場で起きやすい不具合の多くが文字コードに起因しており、避けて通れない知識です。

「文字コード」には非常に長い歴史があり、当時の技術者たちが直面した制約の中で生まれた、苦悩や試行錯誤の積み重ねの産物です。そのため、現在の姿だけを見て「いまいち」「分かりにくい」と単純に批評できません。しかしながら、私たちはこの問題を避けて通ることはできません。そこで、本稿では文字コードの性質と、陥りやすい事象について、ケースごとに対処方法を示します。

前回のシフトJISに続き、今回は文字コード「Unicode」の「UTF-8」について、これだけは知っておいてほしいポイントを解説します。

今回言いたいポイント

#
  • もうシフトJISとかやめてUnicodeに統一したいよね
  • Unicodeを使う上での注意すべきポイント
    • UTF-8におけるBOMについて

Unicodeってなんですか

#

Unicodeは、今や世界標準と言ってよいほど広く浸透しており、Javaの内部でも使用されているため、避けては通れない文字コードとなっています。
正確には、Unicodeとは「文字集合」であり、世界中のあらゆる文字(英数字・記号・ひらがな・カタカナ・漢字など)を扱うことを目的としています。各文字には「コードポイント」と呼ばれる一意の番号が割り当てられており、たとえば「A」(半角のA)は U+0041、「あ」は U+3042 というコードポイントが付けられています。
Unicodeですべての文字を扱えるのであれば、シフトJISなどを使う必要はなく、すべてUnicodeに統一すれば文字コードの問題は解決しそうにも思えますね。

しかし、実際にはUnicodeを使えばすべてが解決するというわけではありません。
今回は、Unicodeのエンコーディング方式のひとつである「UTF-8」で発生しうる問題点について解説します。

UTF-8ってなんですか

#

具体的な問題点を解説する前に、Unicodeのエンコーディング方式である「UTF-8」について簡単に触れてみます。

UTF-8というエンコーディング方式は、Unicodeのコードポイント(U+0000 - U+10FFFF)を、1〜4バイトの可変長で表現します。
たとえば、「A」(U+0041)や「あ」(U+3042)は、次のようにUTF-8で表されます。

  • 「A」(U+0041) : 0x41 (1バイト)
  • 「あ」(U+3042) : 0xE3 0x81 0x82 (3バイト)

UTF-8では、コードポイントの値に応じて必要なバイト数が変わります。

  • 1バイト:U+0000 - U+007F(ASCII)
  • 2バイト:U+0080 - U+07FF
  • 3バイト:U+0800 - U+FFFF(ほとんどの日本語の文字はここに含まれる)
  • 4バイト:U+10000 - U+10FFFF(補助面の文字)

そのため、文字によってUTF-8のバイト列の長さが異なるという特徴があります。

なお、UTF-8の1バイトで表される文字はASCIIと互換性があり、インターネット上ではデファクトスタンダードとして広く採用されています。

BOMとは

#

今回の問題点を解説する前に「BOM」にも触れておきます。
BOM(Byte Order Mark)は、テキストの先頭に付けられるバイト順(エンディアン)を示す目印です。
UTF-16やUTF-32といったエンコーディング方式では、どの順序でバイトを並べるかを指定する必要があります。このバイトの順序のことを「エンディアン」と呼びます。
たとえば、文字「A」(U+0041)はUTF-16では以下のように表されます。

  • ビッグエンディアン(BE):0x00 0x41(上位バイトを先に書く)
  • リトルエンディアン(LE):0x41 0x00(下位バイトを先に書く)

UTF-8におけるBOM

#

UTF-8はエンディアンに依存しないエンコーディング方式であるため、通常はBOMを付与する必要はありません。ただし、ファイルがUTF-8であることを明示する目的で、あえてBOMを付けることもあります。
身近な例としては、ExcelでCSVファイルをUTF-8形式で出力する際、BOMが自動的に付与されます。これが原因で、さまざまな場面で問題の原因となることがあります。

例えば、JSPファイルやThymeleafのテンプレートはHTMLレスポンスを生成します。しかし、UTF-8のBOM付きで記述されていると、HTMLの先頭にあるべき <!DOCTYPE html> の前に 0xEF 0xBB 0xBF が出力されてしまいます。ブラウザによっては、これによりHTMLとして正しく解釈されないことがあります。

また、CSVファイルの読み込み時も注意が必要です。
BOMの存在を想定していないコードでは、先頭の数バイトがゴミとして扱われたり、ヘッダ名を正しく読み取れずエラーになる場合があります。

BOMが厄介な理由

#

この問題の厄介な点は、BOM(Byte Order Mark)がエディタ上では目に見えないことです。
そのため、JSPファイルやCSVファイルでエラーが発生しても、ファイルの先頭にBOMが含まれていることに気づきにくく、原因の特定に遅れるケースが少なくありません。

この記事を読んでくださっている皆さんは、UTF-8のファイルで予期しない問題が発生した際には、「先頭にBOMが含まれていないか」という観点からも調査してみてください。

たとえば、バイナリエディタやcertutil -dump sample.csv コマンドなどで、ファイルの先頭に0xEF 0xBB 0xBFが含まれていないかを確認できます。

この問題への対処

#

さて、BOMが入っていることが問題の原因と判明した場合、取るべき対処は以下の2つに絞られるかと思います。

  • BOMを削除する
  • BOMを読み飛ばす

BOMを削除する

#

これはJSPやThymeleafなどのテンプレートでの対処です。
期待されないBOMが先頭にあることで、ブラウザによるHTMLの解析を妨げているので、BOMを削除してください。
また、HTMLのエンコーディングを指定する場合は、ヘッダータグで<meta charset="utf-8">を指定することを検討してください。

BOMを読み飛ばす

#

意図しないBOMは読み飛ばすこともできます。こちらはファイルの読み込みを実装している側での対処です。
例えば、ユーザーが作成したUTF-8のCSVを取り込むようなシステムにおいては、ユーザーがExcelなどでBOM付きのCSVファイルを生成することを制限できません。
このように外部入力に柔軟に対応したい場合は、先頭にBOMがはいっている「かもしれない」ことを前提にBOMがあれば読み飛ばすように実装することで問題を解決できます。

まとめ

#
  • BOMは目に見えない「地雷」になりやすいため、問題発生時には真っ先にチェックすべきポイントの1つです。
  • アプリケーション側で用意しているリソースであれば削除するべきです。
  • 外部入力情報でありアプリケーション側でコントロールが難しいものであれば、BOMが入ってくることを前提として、BOMを読み飛ばすなどの対処を検討するべきです。

いずれにしても、文字コードやエンコードを統一すればすべての問題を回避できるわけではありません。その前提を踏まえたうえで、採用する文字コードやエンコード方式についての理解を深めてください。
文字コードに関するトラブルも、仕組みを正しく理解して対処できれば、逆にそれが自分の技術的な武器になります。

豆蔵では共に高め合う仲間を募集しています!

recruit

具体的な採用情報はこちらからご覧いただけます。