以前、リリースした rmsmf コマンドや、これから書く記事の理解を助ける為に、日本語の文字コードについてIT素人さん向けに、簡潔に解説しておこうと思う。
shift-jis と utf-8 の混在問題に関する記事(リンクリスト)に戻る
日本語で扱われる文字コード
通常、日本語の OS で扱われる文字コードは以下の物になる。
jis
shift-jis
euc-jp
utf-8
utf-16 LE(Little Endian)
utf-16 BE(Big Endian)
utf-32 LE(Little Endian)
utf-32 BE(Big Endian)
jis
元々、文字コードはASCIIコードと言う 7ビットの文字コードが米国で定められていた。
ASCIIコードは英数字と一部の記号(!”#$%&'()=-~)を定めた、最初の文字コードだ。
日本など世界の国々は英数字と一部の記号に関してはASCIIコードを継承した文字コードを使用している。
しかし、ASCIIコードは7ビットなので、1ビット増やして8ビットの1バイトにしても、256文字しか表せず、平仮名と片仮名に加えて何万文字もの漢字を有する日本語には不十分な文字コード体系だった。
そこで、ASCIIコードの英数字と一部の記号だけ1バイトで表し、それ以外の文字は2バイトで表す文字コードが作られた。
jis コードは ASCIIコードを継承した1バイト文字と、日本語の2バイト文字をエスケープ文字で切り替えて併用する文字コード。
電子メールでは標準規格として長い間使用されていた。
最近はほとんど見かけない。
エスケープ文字で切り替えるため、必ず文字列の先頭から文字列を全部読み込まないと、個々の文字が読めない。 途中から文字列を読むと1バイト文字と、日本語の2バイト文字を区別できない。
shift_jis
jis コードの様に、エスケープ文字を使用する事なく1バイト文字と、日本語の2バイト文字を併用する文字コード体系として ‘ shift-jis ‘ と ‘ euc-jp ‘ が作られた。
shift-jis は主に Microsoft社 と Apple社 が自社 OS に採用し、euc-jp は各社や OSS の UNIX OS で採用された。
つい最近まで shift-jis は Windows や Mac で使用される、日本ではもっとも普及した文字コードだった。
Excel も CSV出力する場合は Excel 2013 までは shift-jis でしか出力できなかった。
Excel 2016 から utf-8 でCSV出力できるようになった。
しかし、今でも Excel は shift-jis の CSV を閲覧・編集・出力できる。
shift-jis はいくつか欠点がある。
2バイト文字コードで、1バイトのASCIIコードを継承し、併存しているため、1バイト文字が158文字、2バイト文字が11233文字しか使えない。
合計しても11391文字しか使えないため、何万文字も文字を有する日本語の一部の漢字しか使うことができない。
私も含めて自分の名前も書けない人も多い。
2バイト目にASCIIコードと同じ番号が来る事があり、先頭から順番に読み込まないと正確な文字コードが、識別し難い。識別はできるが処理が複雑になる。
文字列の途中からデータを取得すると、それが1バイト目か2バイト目か判別できない場合がある。
データ通信などでデータの一部が壊れると、その後ろが全部分からなくなる可能性がある。
また、JIS標準規格はあるが、方言も多く機種依存拡張も多い。この部分は機種が変わると文字化けする。
プログラミング言語などで多用されるエスケープ文字「」が shift-jis 文字の2バイト目に一部使われている為、プログラミング言語側で shift-jis の「」を使用した文字と、エスケープ文字「」を区別する処理を実装しなければならない。
お世辞にも優れた文字コード体系とは言えない。
euc-jp
euc は、AT&T が定めた、国際多言語文字コード。
日本語が euc-jp 、韓国語が euc-kr 、簡体中国語が euc-ck と言語ごとに euc 文字コードが用意されている。
ASCIIコード互換の7ビットは1バイト文字の「0x00」から「0x7F」までで表され、1バイト目の「0x80」から「0xFF」までが日本語(多言語)で表される。
2バイト目も「0x80」から「0xFF」の範囲内で表されるため、1バイトを読み込んだだけでそれが1バイト文字か2バイト文字が明確に区別できる。
半角カナは存在しない。カタカナは全角文字(2バイト文字)だけである。
主に UNIX で使用された。
Linux にも使用されていたが 16ビットPCが主流の時代から32ビットの高価な OS で使用されていたので、あまり庶民には普及していない。
シンプルで優れた文字コード体系であるが、2バイト文字であるが故に何万文字もの漢字を有する日本語には不十分である。
utf とは
utf というのは全て Unicode だ。
詳しい説明は他のサイトで調べて欲しい。
簡単に説明する。
Unicode(ユニコード) というのは世界標準の文字コードの配列を定めた物だ。
要するに文字の「並び順」を標準化したものだ。これをマッピングと呼ぶ。
このマッピングに従って、具体的に文字コードを定義したものが ‘UTF(Unicode Transformation Format)’ になる。
utf-8
utf-8 は基本は 1バイトで文字を表す。
しかし 1バイトでは256種類しか文字を表せないので、7ビットのASCIIコード部分だけ1バイトで表し、それ以外は2バイトから4バイトで表す、可変長の文字コードだ。
0 から 0x7F(127)までの7ビット範囲のマッピング(文字の並び順)を1バイトで表す。この部分は128文字になる。
(0x で始まる数値は16進数)
0x80(128) から 0x07FF(2,047) までの11ビット範囲のマッピングを2バイトで表す。この部分は1,920文字になる。
0x800(2,048) から 0xFFFF(65,535) までの16ビット範囲のマッピングを3バイトで表す。この部分は63,488文字になる。
0x10000(65,536) から 0x10FFFF(1,114,111) までの21ビット範囲のマッピングを4バイトで表す。この部分は1,048,576文字になる。
合計1,112,064文字を表せる文字コード体系となる。
1バイト文字から4バイト文字までのマッピングコードを入れる utf-8 の器を以下に示す。
この器は文字コードを2進数で表す。
x の部分に2進数マッピングコードを右詰で入れる。(左側空き部分は 0 詰め)
0 や 1 で書かれた部分は固定値になっている。x の部分にだけマッピングコードをはめ込む。
(1バイトは8ビットです。バイト単位で「,」区切りで見せます。分かりやすくするため、固定値との区切りを「-」で表記します。)
1バイト文字(07ビット) : 0-xxxxxxx
2バイト文字(11ビット) : 110-xxxxx,10-xxxxxx
3バイト文字(16ビット) : 1110-xxxx,10-xxxxxx,10-xxxxxx
4バイト文字(21ビット) : 11110-xxx,10-xxxxxx,10-xxxxxx,10-xxxxxx
例えば2バイト文字のマッピングコード「0x80」は、11ビット2進数で「001,0000000」なので、
器「110-xxxxx,10-xxxxxx」に右詰で「110-00100,10-00000」と、はめ込む。
2進数「11000100,1000000」なので、16進数「C4,40」が utf-8 のコードとなります。
3バイト文字のマッピングコード「0xFFFF」は、16ビット2進数で「11111111,11111111」なので、
器「1110-xxxx,10-xxxxxx,10-xxxxxx」に右詰で「1110-1111,10-111111,10-111111」と、はめ込む。
2進数「11101111,10111111,10111111」なので、16進数「EF,BF,BF」が utf-8 のコードとなります。
プログラムが文字を読み込んだとき、先頭ビットが「0」ならば、1バイト文字となる。「1」ならばそれ以外。
先頭が「110」なら2バイト文字の先頭バイト。
先頭が「1110」なら3バイト文字の先頭バイト。
先頭が「11110」なら4バイト文字の先頭バイト。
先頭が「10」なら複数バイト文字の先頭以外のバイト。
このようにバイト単位で明確に区別できる。
プログラムが文字列の途中からバイトを読み込んでも誤読する事がない。
通信などでデータの途中が破損しても、破損するのはその文字だけで、他の文字には影響しない。
非常に優れた文字コード体系である。
可変長なので文字数は、文字列を全部読み込んで見ないと分からないのが欠点だ。
utf-16
同様に utf-16 は基本は 2バイトで文字を表す。2バイトでは65536文字しか表せないので、それを超える文字数は4バイトで表す。(この4バイト領域の事を ‘surrogate pair:サロゲートペア’ と呼ぶ)
コード体系は urf-8 よりは簡単である。
Unicodeマッピングコードの「0x0000」から「0xFFFF」の範囲のコードはそのまま2バイトの utf-16 文字コードとして使用される。
この範囲のマッピングコードの内、「0xD800」から「0xDFFF」の範囲のコードには文字が割り当てられていない。ワザとUnicodeを空白にしてある。
「0xD800」から「0xDFFF」の範囲のコードは2つ(2 + 2 = 4バイト)を組み合わせて、utf-16 文字コードを形成する。
範囲を、前半「0xD800」から「0xDBFF」の1024個と、後半「0xDC00」から「0xDFFF」の1024個を組み合わせ、1,024 × 1,024 = 1,048,576 文字を表す。
これにより、Unicodeマッピングコードの「0x10000」以降の utf-16 文字コードを定義する。
utf-16 文字コードの最大総数は、
65,536 – 1,024 – 1,024 + 1,048,576 = 1,112,064 文字
と、utf-8 と同じである。
元々、utf-16 が策定されてUnicodeマッピングが定まったので、utf-8 の方がそれに合わせている。
「0xD800」から「0xDFFF」の範囲のコードなら4バイトコード(サロゲートペア)、それ以外なら 2バイトコードと明確に区別できる。
また、「0xD800」から「0xDBFF」の範囲なら前半(ハイサロゲート)、「0xDC00」から「0xDFFF」なら後半(ローサロゲート)と区別できる。
utf-16 もプログラムが文字列の途中からバイトを読み込んでも誤読する事がない。
通信などでデータの途中が破損しても、破損するのはその文字だけで、他の文字には影響しない。
非常に優れた文字コード体系である。
utf-32
utf-32 は全ての文字を 4バイトで表す、固定長文字コードになる。
Unicodeマッピングコードが、そのまま utf-32 文字コードになる、もっとも単純な文字コード体系だ。
バイト数から文字数が簡単に割り出せるので、DBの内部データなどに使用される。
BE-ビックエンディアンと、LE-リトルエンディアン
2バイト以上の文字コードの場合、一つの文字コードの中でバイトの並び順が、前から順番に並ぶものを BE(big endian:ビックエンディアン) と呼び、後ろから並ぶものを LE(little endian:リトルエンディアン) と呼ぶ。
CPU の仕組みにより、バイト並び順が逆向きになっていた方が、CPUで処理し易い事が多いため BE と LE が用意されている。
現代の32ビットや64ビットCPUは、記憶領域のレジスタに、メモリからデータを読み込む場合、32ビットなら4バイト、64ビットなら8バイトのデータを一括で読み込む事になる。
一方、メモリアドレスは1バイト単位で指定するため、64ビット8バイトのデータをメモリから読むとき、「アドレスの前から順番に8バイト読む」か「アドレスの後ろから順番に8バイト読む」か、CPUによって規則が決められている。
メモリは若いアドレス番号から読むので、
BE 場合、レジスタの左側、つまり桁の大きい方から詰めていく。
(64ビットの2進数領域で考えてください。8ビット単位で8回読み込みます)
LE 場合、レジスタの右側、つまり桁の小さい方から詰めていく。
だから数値や文字コードなどは、初めから CPU で読みやすい順番でデータを並べておいた方が良い。
それが BE や LE と言った「endian:エンディアン」という概念である。
インテルのCPUなどは LE が処理し易い。(Windows 系 PC)
昔のモトローラーのCPU(Power PC)などは BE が処理し易い。(昔、Mac が使用していた、今はインテル)
ARM はどちらも選べるようになっているらしい。(スマホ)
utf-16 と utf-32 にはそれぞれ BE(big endian:ビックエンディアン) と LE(little endian:リトルエンディアン) の実装がある。
encoding:エンコーディング
最近、文字コードを表す言葉として encoding:エンコーディング という言葉が使われる事が多くなってきた。
本来、エンコーディングは文字コードを表す言葉ではない。
元々はデータ通信の言葉で、コンピュータでは通信やデータ保存する場合、情報を決められた規格に従って変換して、送受信したり、保存や読み込みを行う。
規格が決まっていないと、送信側と受信側で情報を共有できないからだ。
この規格に合わせて情報を信号に変換することを「符号」と呼び、受信側で信号から情報を逆に変換して情報を取り出す事を「復号」と呼ぶ。
英語では、「符号」は encode:エンコード、「復号」は decode:デコード と呼ぶ。
符号化する事を encoding:エンコーディング と呼び、復号化する事を decoding:デコーディング と呼ぶ。
これは電波に信号を載せる場合でも、暗号化する場合でも、文字列をファイルやDBに保存する場合でも、「符号」「復号」という言葉を使用する。
最近、Unicode という文字のマッピング(符号化文字集合)と、マッピングの符号化方式が異なる文字コードが登場した事により、マッピングの符号化方式を示す為に encoding:エンコーディング という言葉を必要とするようになった。
Unicode は文字コード(符号化文字集合)であるが、utf-8,utf-16LE,utf-16BE,utf-32LE,utf-32BE と複数のエンコーディング方式が存在する為に、文字コードだけではどの「utf」なのか識別できない。
だから、最近は文字コードを表す時にエンコーディング方式を示す必要があるのだろう。
現代では、文字コードの代わりに encoding という言葉が使われる事が多い。
文字コードとエンコーディング方式の違いは把握しておいた方が良い。
BOM (byte order mark)
BOM:バイトオーダーマーク というのは、Unicode テキストにおいてどの「utf」エンコーディング方式を使用しているかを区別する為の、頭記号である。
テキストファイルの場合、先頭の2バイトから4バイトにエンコーディング方式を表す記号を付けて、utf を識別する事ができる。 通常は utf テキストファイルは、先頭に BOM を付けるのが正しい。
しかし、現実には BOM の無いテキストファイルも多い。 明確に義務づけられていないからだ。
BOM は以下の記号が定められている。 0x で始まる数値は 16進数で、1バイト単位で表記している。
UTF-8 = { 0xEF, 0xBB, 0xBF }
UTF-16_Little_Endian = { 0xFF, 0xFE }
UTF-16_Big_Endian = { 0xFE, 0xFF }
UTF-32_Little_Endian = { 0xFF, 0xFE, 0x00, 0x00 }
UTF-32_Big_Endian = { 0x00, 0x00, 0xFE, 0xFF }
この頭記号で、テキストファイルがどのエンコーディング方式を使用しているが区別する。
これが無いと自動判別はできない。
Excel も BOM のない CSV ファイルを読むときは shift_jis と解釈する。
utf-8 で読み込ませたい時は、先頭に{ 0xEF, 0xBB, 0xBF }を付ける必要がある。
以上
日本の主要な文字コードについて必要な事は、これで説明したはずです。
少なくとも、文字コードに関してはこれで、ITエンジニアと会話ができるはずです。