文字コードまとめ

幾度となく技術者たちを悩ませてきた文字コード… このあたりの知識は何度調べても毎回忘れてしまいます. というわけで調べたことをまとめて…というか並べただけです.

文字集合

US-ASCII

制御文字, 数字, アルファベット, 記号類. 7bitの範囲に収められている.

コード範囲 文字種
00-1F 制御文字
20 空白
21-7E 図形文字
7F DEL

ISO-8859-1

Latin-1と呼ばれることもある. US-ASCIIを8bitに拡張したもの. 後半部分にはアクセントやウムラウト付きの文字や, 各国の通貨単位が収録されている.

JIS X 0201

8bit, 制御文字を除いて160文字.

前半半分の7bitはほとんどUS-ASCIIと同じ. 後半にはカタカナや句読点が収録されている.

前半7bitとUS-ASCIIの差異は以下の2つ.

コードポイント JIS X 0201 US-ASCII
5C ¥(円記号) \(バックスラッシュ)
7E  ̄(オーバーライン) ~(チルダ)

JIS X 0208

第二水準までの漢字など, 6879文字が収録されている(参考).

16bit. 正確に言うと, 基本的には7bit2バイト符号もしくは8bit2バイト符号. 図形文字のうちのSPACEと制御文字は1バイトで表現される.

8bitの数字を4桁ずつ分け, 「列/行」のように表現する方法がある. たとえば

0100 0101

は上位と下位の4bitをそれぞれ10進法に直すと4, 5であるため, 4/5と表現される.

第一バイトを同じくする文字たちを「区」といい, 区の各符号を「点」という. 第一バイトと第二バイトはそれぞれ2/1から7/14までのbit組み合わせが許される. したがって区は94通りあり, 各区に点は94個あることになる.

この文字集合にはアルファベットやカタカナも収録されているが, JIS X 0201とは異なる符号化がなされている. 一般的には0201のものを半角, 0208のものを全角と呼び分けている.

JIS X 0213

JIS X 0208を包含する文字集合. 第三水準と第四水準の漢字が追加された. 11,233文字.

マイクロソフト標準キャラクタセット

16bit. JIS X 0201, JIS X 0208, NEC拡張文字, IBM拡張文字を統合したもの.

NEC拡張文字としては丸数字, IBM拡張文字としてははしご高などがある.

Unicode

21bit. U+XXXXという形式で表される(XXXXは4桁から6桁の16進数).

もともと16bitで世界中の文字を表現する予定だったが, のちに21bitに拡張された. 16bitまでの範囲をBMP(Basic Multilingual Plane)という.

文字エンコーディング

Shift_JIS

JIS X 0201の空き領域にJIS X 0208をマッピングする. 文字集合としてマイクロソフト標準キャラクタセットを用いる場合は, CP932と呼ばれる.

JIS X 0201に収録されている1バイト文字はそのまま1バイトに符号化される.

JIS X 0208に収録されている2バイト文字は, JIS X 0208のコードポイントとはことなる値に符号化される. 先行バイトは0201が使わない数値を割り当てることにより, 2バイト文字なのかを識別することができる.

しかし狭い領域に詰め込んだことにより, 以下の領域が重複している.

  • 1バイト文字と2バイト文字の後続バイト
  • 2バイト文字の先行バイトと後続バイト

したがってShift_JISの処理が不完全な場合, 意図しない符号化を生んでしまうこともある.

たとえば「ラ」の後続バイトと「リ」の先行バイトを並べると「宴」になってしまう.

文字 符号
83 89
83 8A
89 83

対処するには, マルチバイト文字に対応させること.

EUC-JP

Unix上で日本語を扱うために作られたエンコーディング.

US-ASCIIとJIS X 0208を符号化. JIS X 0201のカタカナ(いわゆる半角カタカナ)も含めることができる.

US-ASCIIはそのままコードポイントが使われ, 1バイトで表現される.

1バイトのうちUS-ASCIIが使わない後半部分を使い, 2バイト文字が表現される. しかし先行バイトと後続バイトが使う領域に重複がある. したがってShift_JISのような「ラリ宴」問題が発生しうる.

ISO-2022-JP

7bitの文字エンコーディング. エスケープシーケンスという符号により, 2つの文字集合(US-ASCIIとJIS X 0208)を切り替える方式.

JIS X 0201のカタカナには対応していない.

歴史的な理由から, 主に電子メールで使われてきた.

UTF-16

もともとUnicodeは16bitの範囲で収めようと考えていたため, それをそのまま利用する方式(UCS-2)が使われていた. その後BMP以外の文字をサポートする符号化方式として, UTF-16が考案された.

BMP以外の文字は, Unicodeの空き領域(U+D800~U+DFFF)をペアにして表現する. これをサロゲートペアという.

UTF-16には2バイトの並べ方としてビッグエンディアンとリトルエンディアンの2種類がある. たとえばコードポイントがU+3042である「あ」の場合は以下のような違いがある. ビッグエンディアンはそのままだが, リトリエンディアンは2つのバイトが逆に並んでいる.

符号
ビッグエンディアン 3042
リトルエンディアン 4230

リトルエンディアンはコンピュータには分かりやすいらしい. どちらの方式で符号化されているかを表すのに, BOMが利用される.

UTF-8

US-ASCIIと互換性がある. 1バイトから4バイトの可変長.

スカラ値 符号化パターン
0~7F 0xxxxxxx
80~7FF 110xxxxx 10xxxxxx
800~FFFF 1110xxxx 10xxxxxx 10xxxxxx
10000~10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

例えば円記号(¥)のコードポイントはU+00A5である. これを2進数に直すと10100101である. 上部を0で埋めて11桁にすると00010100101となる. これを5桁と6桁に分けて上の表に当てはめると

11000010 10100101

となり, 16進数に直すと0xC2 0xA5となる.

UTF-8には非最短形式という問題がある. 例えば上の円記号の例でいうと, 00A5の2進数表記は

0000 0000 1010 0101

とすることもできる. すると上の表の3段目に当てはめて,

11100000 10000010 10100101

と3バイトで表すこともできてしまう.

BOM

Byte Order Markの略. テキストデータの先頭に付け加えられる数バイトのデータのこと. これにより, エンコーディングとしてUTFを使用していること, またどのUTFなのかを区別できるようにしている.

UTF-8はビッグ/リトルエンディアンの区別はないため, 単にUTF-8を使っていることを宣言しているだけ. ただしBOMをつけることを許容しているだけであって, 推奨しているわけではないらしい. むしろWebに関わるファイルにはつけないほうがいいみたい.

符号化形式 エンディアン BOM
UTF-8 なし 0xEF 0xBB 0xBF
UTF-16 BE 0xFE 0xFF
LE 0xFF 0xFE

Microsoft ExcelはデフォルトではShift_JISとしてファイルを読み込む. したがってUTF-8でエンコードしたCSVファイルを開くと文字化けを起こす. しかしBOMをつけていればこれは防ぐことができる.

参考

BOMなしUTF-8やShift_JIS, EUC-JPはUS-ASCIIと互換性がある(ただしShift_JISは2文字だけ異なる)ため, 英数字のみが書かれたファイルの場合, これらの文字コードに区別はない.

したがって, 英数字のみを書いたファイルにfileコマンドを使うと, 以下のように表示されることがある.

README.md: ASCII text

改行コード

CR(Carriage Return)とLF(Line Feed)の2種類がある. システムやソフトウェアによって, 片方もしくは両方を用いる.

US-ASCIIではCRは0x0D, LFは0x0Aである.

  • LF: Unix系
  • CR+LF: Windows

Rubyの正規表現では以下のようなメタ文字が用意されている.

  • CR: \r
  • LF: \n

判別方法

fileコマンドで文字コードと改行コードを教えてくれる. ただし改行コードについてはCRLFのときだけ表示がある.

nkfコマンドは文字コードや改行コードの変換もできる. ただしMacには標準でインストールされていない. 以下でインストール.

$ homebrew install nkf

使ってみる

$ nkf --guess foo.md 
UTF-8 (LF)

文字列の置換であれば, trコマンドを使うこともできる.

PostgreSQLでは\lによりデータベースの情報が取得できる. これにより文字コードも確認できる.