フラミナル

考え方や調べたことを書き殴ります。IT技術系記事多め

UTF-8の「BOMあり/なし」とは何なのか?

Emoji esculptures, República Subway Station, São Paulo, Brazil.

この記事で確認したいこと

明らかにすること

  • UTF-8のBOMとは何か?
  • UTF-8のBOMあり/なしはどっちがいいのか?

先に結論

  • UTF-8のBOMとは何か?
    • アプリが読み込む文章が「なんの符号化コード・文字集合で書かれているのか」を識別するためのもの
  • UTF-8のBOMあり/なしはどっちがいいのか?
    • UTF-8を使用するアプリによるので調査の必要あり
    • ただしだいたいのケースで不要

前提知識

パソコン上ではすべてが0/1で管理されています。では一体どうやって文字を扱っているのでしょうか?

次の3つの要素が関わってきます。

  • 文字コード
    • 文字1つ1つに割り当てている番号です
    • 例:あ=3042
  • 文字集合
    • 番号と文字の対応表のことを指します
    • 文字コードをまとめているものです
    • 代表的なものにUnicodeASCIIがあります
  • 符号化方式
    • 符号化とはデータを規則にのっとってデジタルデータに変換することです
    • 英語ではエンコーディングとも言います
    • 基本情報を勉強すると音声データを0と1のデジタルデータに変換することで馴染みがあります
    • 文字の世界では文字集合で定義された番号(例:あ=3042)をパソコンが理解できるように0と1にしてあげる役割を担います
    • 代表的なものにUTF-8Shift_JISがあります

参考:文字集合(CCS)と符号化方式(CES)とは−教えて!HELPDESK

イメージとしてはこちらのサイトで紹介されている図がわかりやすいですね。

f:id:lirlia:20200328043544p:plain

利用する符号化方式(UTF-8/16)によってPCが理解するバイト文字列が異なっていますね。

書籍でしっかり抑えるならこちらがおすすめ。

そもそもなぜ符号化方式文字集合に別れているのか?についてはASCIIという文字コードの制約から、Shift_JIS/EUC-JP/ISO-2022-JPが生まれた背景に由来しているとのこと。

参考:Unicodeとは? その歴史と進化、開発者向け基礎知識 - Build Insider

文字が欠落する原因

Webサイトをみていて文字化けをする場面に遭遇した経験はありませんか?

これらも符号化方式や文字集合によって引き起こされています。

例えばネット閲覧における文字化け問題はブラウザが指定した「符号化コード(UTF-8)」がHTMLファイル自体の文字コードが異なるせいで起きています。

HTMLファイル自体が仮に「UTF-8」という符号化コードで保存されていた場合の動作は以下の通り。

  1. HTMLファイルを取得
  2. HTMLファイルの中身をチェック(0/1の連番を見る)
  3. 2でチェックした中身を符号化コードでデコード(エンコードの逆)
  4. 3を文字集合(Unicodeなど)で変換し表示

という流れで動いてくれます。

このときダウンロードしたHTMLファイルがShift_JISにも関わらず、UTF-8で表示してしまうと3のデコードでめちゃくちゃな結果となり、4の文字集合変換で存在しない文字が選択されたり適当な文字が表示されることになります

HTML文字化け実験

こんな感じのHTMLを用意してUTF-8で保存しましょう。

<html>
  <head>
    <title>UTF-8へようこそ</title>
  </head>
  <body>
    <p>こんにちは</p>
  </body>
</html>

そしてブラウザで開きます。 するとちゃんと「こんにちは」が表示されますね。

f:id:lirlia:20200328054145p:plain

これはブラウザが適切にUTF-8だなということを理解してくれたからです。

では、次に<meta charset="shift_jis">を追加してみましょう。(ファイルはUTF-8のままにしてください)

これはブラウザに対して「このファイルはShift_JISで書かれているよ!」を伝えます。

<html>
  <head>
    <title>UTF-8へようこそ</title>
    <meta charset="shift_jis"> 
  </head>
  <body>
    <p>こんにちは</p>
  </body>
</html>

この状態で更新してみましょう。

すると文字化けしちゃいます。

f:id:lirlia:20200328054414p:plain

これは先ほど説明した

  1. HTMLファイルを取得
  2. HTMLファイルの中身をチェック(0/1の連番を見る)
  3. 2でチェックした中身を符号化コードでデコード(エンコードの逆)
  4. 3を文字集合(Unicodeなど)で変換し表示

のうちの3にて「Shift_JISって言われて使ったのに、ファイルがUTF-8だったからデコードがめちゃくちゃになった」ということです。

BOM(バイトオーダーマーク)とは?

さてそろそろ本題に入りましょう。

wiki - バイトオーダーマークが丁寧に説明してくれているのでこれを読み解きましょう。

するとこのように書かれています。

プログラムがテキストデータを読み込む時、その先頭の数バイトからそのデータがUnicodeで表現されていること、また符号化形式(エンコーディング)としてどれを使用しているかを判別できるようにしたものである

要するにBOMの有無によって今から読み込む文章がUnicodeだよということと、UTF-xxだよを明らかにしているわけですね。

具体的にいうと以下のbom.txtのef bb bfがBOMですね。

examination$ cat bom.txt 
<feff>hi

examination$ hexdump -C bom.txt 
00000000  ef bb bf 68 69 0a                                 |...hi.|
00000006

実験(BOMがある/ないファイルを作ってみる)

この記事(bashでもUTF-8 with BOMなファイルを作りたい - Qiita)を参考にbashでBOMありのファイルを作ります。

BOMありのファイルを生成するコード

#!/bin/bash
function f()
{
  echo -en '\xef\xbb\xbf'
  echo $1
}
f $1 > bom.txt

BOMなしのファイルを生成するコード

$ echo "hi" > no-bom.txt

結果

BOMあり

examination$ ./utf-bom.sh "hi"
examination$ cat bom.txt 
<feff>hi
examination$ hexdump -C bom.txt 
00000000  ef bb bf 68 69 0a                                 |...hi.|
00000006

BOMなし

examination$ echo "hi" > no-bom.txt
examination$ cat no-bom.txt 
hi
examination$ hexdump -C no-bom.txt 
00000000  68 69 0a                                          |hi.|
00000003

ちなみに

examination$ file ./*
./bom.txt:    UTF-8 Unicode (with BOM) text
./no-bom.txt: ASCII text

fileコマンドで見てみるとBOMなしのファイルはASCIIと表示されていますね。

fileコマンドではBOMなしならASCIIとして扱い、(UTF-8の)BOMありならUnicode UTF-8としていますね。

ということでUTF-16のBE(ビッグエンディアン)を示す0xFE 0xFFを入れてみたらどうなるのかやってみました。

wikiのここの箇所を参考にしています。 f:id:lirlia:20200328053549p:plain

examination$ echo -en "\xfe\xff" > utf16be-bom.txt
examination$ echo hi >> utf16be-bom.txt
examination$ file ./*bom*
./utf16be-bom.txt: Big-endian UTF-16 Unicode text, with no line terminators

今度は Big-endian UTF-16 Unicode textと表示されましたね。fileコマンドちゃんと仕事するやん!

BOMを使うべきかどうか?

受け取ったファイルに対して符号化コードを適用するのは各アプリ(例えばメモ帳やワード)です。そのためBOMの使用有無はアプリに準拠します。

例えばXMLのRFCではUTF-16で符号化する場合にBOMを必須としているため、RFCに準拠したアプリであればBOMありで保存しなければなりません。

またJSONの場合は、ネットワークで送信する場合はつけてはいけないことになっています。

ですのでBOMの必要有無は実際に使うシチュエーションに応じて調査の必要があります。ただしほとんどのケースはBOMは不要とのことです。

書籍でしっかり抑えるならこちらがおすすめ。