この記事で確認したいこと
明らかにすること
- UTF-8のBOMとは何か?
- UTF-8のBOMあり/なしはどっちがいいのか?
先に結論
- UTF-8のBOMとは何か?
- アプリが読み込む文章が「なんの符号化コード・文字集合で書かれているのか」を識別するためのもの
- UTF-8のBOMあり/なしはどっちがいいのか?
- UTF-8を使用するアプリによるので調査の必要あり
- ただしだいたいのケースで不要
前提知識
パソコン上ではすべてが0/1で管理されています。では一体どうやって文字を扱っているのでしょうか?
次の3つの要素が関わってきます。
- 文字コード
- 文字1つ1つに割り当てている番号です
- 例:あ=3042
- 文字集合
- 番号と文字の対応表のことを指します
- 文字コードをまとめているものです
- 代表的なものに
Unicode
やASCII
があります
- 符号化方式
- 符号化とはデータを規則にのっとってデジタルデータに変換することです
- 英語ではエンコーディングとも言います
- 基本情報を勉強すると音声データを0と1のデジタルデータに変換することで馴染みがあります
- 文字の世界では文字集合で定義された番号(例:あ=3042)をパソコンが理解できるように0と1にしてあげる役割を担います
- 代表的なものに
UTF-8
やShift_JIS
があります
参考:文字集合(CCS)と符号化方式(CES)とは−教えて!HELPDESK
イメージとしてはこちらのサイトで紹介されている図がわかりやすいですね。
利用する符号化方式(UTF-8/16)によってPCが理解するバイト文字列が異なっていますね。
書籍でしっかり抑えるならこちらがおすすめ。
そもそもなぜ符号化方式と文字集合に別れているのか?についてはASCIIという文字コードの制約から、Shift_JIS/EUC-JP/ISO-2022-JPが生まれた背景に由来しているとのこと。
参考:Unicodeとは? その歴史と進化、開発者向け基礎知識 - Build Insider
文字が欠落する原因
Webサイトをみていて文字化けをする場面に遭遇した経験はありませんか?
これらも符号化方式や文字集合によって引き起こされています。
例えばネット閲覧における文字化け問題はブラウザが指定した「符号化コード(UTF-8)」がHTMLファイル自体の文字コードが異なるせいで起きています。
HTMLファイル自体が仮に「UTF-8」という符号化コードで保存されていた場合の動作は以下の通り。
- HTMLファイルを取得
- HTMLファイルの中身をチェック(0/1の連番を見る)
- 2でチェックした中身を符号化コードでデコード(エンコードの逆)
- 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>
そしてブラウザで開きます。 するとちゃんと「こんにちは」が表示されますね。
これはブラウザが適切に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>
この状態で更新してみましょう。
すると文字化けしちゃいます。
これは先ほど説明した
- HTMLファイルを取得
- HTMLファイルの中身をチェック(0/1の連番を見る)
- 2でチェックした中身を符号化コードでデコード(エンコードの逆)
- 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のここの箇所を参考にしています。
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は不要とのことです。
書籍でしっかり抑えるならこちらがおすすめ。