やる気がストロングZERO

やる気のストロングスタイル

文字コードまわりを知ってる範囲でまとめた

文字コードまわりって普段あんまり気にしなくても問題ないけど、たまにこれ絡みでの問題があったときに構造を理解してないとどう手をつけていいかわからなかったりするので、自分が理解している範囲で書いてみる。

参考)

文字が表示されるまでの流れ

コンピュータが記録したり扱ったりできるのはバイナリ(0101みたいな)である。
例えば「あ」という文字をコンピュータで扱うにはバイナリとして扱う必要がある。
なので、「あ」 => 「00000001」, 「い」 => 「00000010」のように、文字とバイナリの対応表を前もって用意しておく。
そうすると、00000001があったときには「あ」と表示すればいいし、00000010があったときには「い」として表示すればいい。

上記したルールは今適当に割り振っただけなので一般的には使えないが、一般的に浸透しているルールがあって、それがShift-jisとかUTF8とかである。

Shift-jisだと 「あ」は「10000010 10100000」、「い」は「10000010 10100010」だし、
UTF8では「あ」は「11100011 10000001 10000010」、「い」は「11100011 10000001 10000100」となる。
Ubuntuだと基本文字コードがUTF8なのでターミナルで確認できる。

$ echo あ | od -tx1
0000000 e3 81 82 0a
0000004

16進数で表示されているが、それぞれ

e3 => 11100011
81 => 10000001
82 => 10000010

で「あ」の文字コードになっている。(最後の0aは改行コードである)

コンピュータがテキストファイルを読み込む時、どの文字エンコードを使うかを予め指定しておく。(テキストエディタでUTF8とか指定されているのはこの部分)
コンピュータは読み込むデータの中に 「11100011 10000001 10000010」というデータをみつける。
コンピュータはこれをUTF8の対応表にあてはめて、これは「あ」だと認識する。
※ 実際はコンピュータが認識するというより、UTF8の対応表に当てはめて、フォントデータの中から対応する文字の画像を表示しているだけだと思う。

ASCII

ちなみに英語でよく使われるアルファベッドだけで構成されているテキストはASCIIという文字エンコードが使われている。 Shift-jisもUTF8もこのアルファベッド部分はASCIIと全く同じ文字コードになっていて互換性がある。
だから、ASCIIに含まれる文字だけで構成されているtextはASCII、UTF8、Shift-jisのどれを使って文字エンコーディングしても問題ない。

文字化けの仕組み

文字が意味不明な漢字の羅列になってしまって「文字化けした」ってなる状態がある。なにが起こっているのか?
これはバイナリの文字データ(例えばUTF8の「あ(11100011 10000001 10000010)」)を誤った文字エンコード(例えばShift-jis)の対応表を使って文字に対応づけた時に、「あ」ではなく別の文字が対応付けられてしまうことから起こる。

エディタの文字エンコード判定(詳しくない。聞いた程度の知識。間違ってるかも)

上記の文字化けは、入力時に指定されている文字エンコードと、出力時に指定されている文字エンコードが異なる場合に起こるが、最近はエディタが自動的に文字エンコードを判定してくれて表示されるのであまり文字化けに遭遇することもなくなった。

ではエディタはどうやって文字エンコードを判定しているのか?

エディタで開くようなシンプルなtextファイルの場合は基本的に文字エンコードの情報を持っていないので「わからない」のだが、バイト列の特徴からあたりをつけて判断したりしているらしい。 なので、あまりに入力している文字数が少なく、判断材料が少ないと誤った文字エンコード判定が行われる場合もあるっぽい。

(一部のエンコードには先頭に特徴的なバイト列があるので、それが判断条件になっていたりもするっぽい。)

ユニコードについて

昔は各言語ごとに文字エンコードが存在した。
日本語はShift-jisはEUC-JPなど。
韓国には韓国の文字エンコードが、アラビアにはアラビアの文字エンコードがあった。
Shift-jisにはアラビアの文字コードは入ってない。
アラビア語で書かれた文字を表示するにはアラビア語の文字エンコードを指定する必要がある。

じゃあ、日本語とアラビア語の両方が含まれた文字を表示させるにはどうすればいいのか?

こんな感じ「こんにちは!مرحبا!」 (ここでは実現できている)

詳しくはうろ覚えだが、言語が切り替わる位置で特定のバイト列(言語切替用)を挿入することで、「ここからはアラビア語の文字エンコード」という感じで実現していたっぽい。

こういうのもなかなか大変だということ(なのかどうかよくわからんが)でユニコードが誕生した。
ユニコードは全世界の文字を一つの文字エンコードに含んでしまおう、というようなやつ。
日本語も韓国語もアラビア語もここに入っている。
だからユニコードの文字エンコードの1つであるUTF8で書かれているこの文章では「こんにちは!مرحبا!」は特に切り替えバイトなどなくとも単純に文字エンコードの対応表で変換する事が可能になっている。
ユニコードには絵文字も入ってるので、昔あった「docomoとjphoneでは絵文字のやり取りができない(昔は絵文字は各自独自でエンコード領域を拡張して使っていたため互換性がなかった)」というような状況は今は起きていない。今はPCでもスマホでも共通の絵文字が使えるようになっている。

ユニコードとUTF8, UTF16とか

ユニコードは上記した通り「全世界の文字を一つのコード表にのせてしまおう」という表になっている。
ここに掲載された各文字を「どのようなバイト列に当てはめるか」はまた別の話になる。(ややこしいが)
「どのようなバイト列に当てはめるか」の部分が文字エンコードで、UTF8やUTF16やUTF32(他にもあるのかも?)になる。

たとえば、ユニコードに掲載された「あ」をUTF8を使ってバイト列にすると「11100011 10000001 10000010」になるし、
UTF16を使ってバイト列にすると「00110000 01000010」になる。

ちなみにUTF16の「あ」のバイト列はユニコードのコードポイントと同じである。
コードポイントとはユニコードに掲載された文字に割り振られるIDみたいはやつで、バイト列とは無関係だがこういう感じで同じになっているものもある。(UTF32だと全く同じになるらしい。)
バイト列変換ルールはサロゲートペアとかあってややこしくて詳細には理解できてない。
UTF8とかは、文字によってバイト列が2バイトだったり3バイトだったりするので、このあたりの事情で「文字数を数える」という処理がなかなか難しい場合がある事を気に留めておくといい。(単純に1文字 = 2バイトってやるとバグる場合がある。)

フォントについて

文字を表すバイト列を文字エンコードを使って何の文字を表すのかわかった。
次はディスプレイにこの文字を表示するだが、ここでフォントが必要になる。
フォントは文字の形を表す画像データで、「あ」のバイト列に対応するフォントデータ「あ」がまさに画面に表示される。

ユニコードが使われるようになって全世界の文字を扱う事ができるようになったが、これを表示できるかどうかは、その文字を表すフォントが利用できるかどうかによる。
先程アラビア語(مرحبا!)が表示できていたが、これはあなたのデバイス(PCかスマホか知らないが)にアラビア語のフォントが入っているから表示できている。(もしかしたらブラウザ表示時にweb上から必要なフォントをダウンロードする仕組みがあったりするかも)
今どきはデフォルトで全世界の言語のフォントが入っているっぽい。詳しくは知らない。

自分が知ってるのはそんな感じだが、参考にした以下の本をみるともっと泥臭い話とかあってとてもおもしろいのでものすごくおすすめです!