文字コードutf8をphpで学ぶ

はじめに

農学部卒の私がWebシステム開発会社に入社したての頃、文字化けを起こす理由がさっぱりわからなかったのですが、phpで実際に文字を出力して初めて
「文字化けする理由ってこういうことか!!」とわかりました。
文字コードの仕組みがどういうものか知っていると役立つことが多いので記事にしました。

パソコンが文字を表示する仕組み

パソコンは0と1の2進数しか扱えません。
0と1がどうやって文字になっているのでしょうか?
正解は、文字に数字を割り振って、この数字の場合はこの文字を表示するというように、番号を振っています。

文字に番号を振った表のことを文字コード表と言うのですが、一番ベースになっている文字コード表がasciiコード表です。
asciiコード表では、例えば、「a」という文字は65番目の数字が割り振られています。

具体的にディスプレイに表示するまでの流れを説明すると
「00100001」という2進数がが65という数字で解釈され、その数字の文字コード表と照合されて、asciiコード表なら65番目の「a」 という文字だということになります。

ただ、aという文字を表示するとなったときに、パソコンは数字しかわからないので何を表示して良いか、わかりません。
そこで、数字と文字の表示情報を組み合わせた、「フォント」という文字情報を使って表示します。

フォントとは、具体的に「明朝体」、「ゴシック体」などのことです。
フォントにはGID/CIDというIDが振られていて、そのIDと文字コードをマッピングしたものをCMapといいます。
例えば、明朝体で表示、asciiコードでデータを扱い「00100001」この数値情報を表示してほしいとなった場合
「00100001」
→65
→asciiコード変換用のCmapを参照して、明朝体のGID/CIDを抽出
→明朝体の「a」が表示される

という流れになります。

◆参考サイト
http://www3.nit.ac.jp/~tamura/ex2/ascii.html
https://www.morisawa.co.jp/culture/dictionary/1902

文字を出力してみる
utf8で文字を出力させる、phpをブラウザで表示しましょう。
まず、utf8.phpというファイルを作成します。
下記のプログラムを下記のコードを入力します。

<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <table>
    <?php
    $i = 0;
    print '<tr>';
    print '<td></td>';
    print '<td></td>';
    while ($i < 16) {
        print '<th>' . $i . '</th>';
        $i++;
    }
    print '</tr>';
    // 1バイト文字出力(asciiコード)
    $oneByte = 0;
    $num = 0;
    while ( $oneByte < 128) {
        if ( bcmod($num , 16) == 0) {
            print '<tr>';
            print '<th>' . dechex($num) . '</th><th>' . $num . '</th>';
        }
        echo '<td>' . chr($oneByte) . '&nbsp;</td>';

        if (bcmod($num , 16) == 15 ) {
            print '</tr>';
        }
        $oneByte++;
        $num++;
    }
    // 2バイト文字出力
    $twoByte = 194;
    while ( $twoByte < 224) {
        $twoByte2 = 128;
        while ( $twoByte2 < 192) {
            if ( bcmod($num , 16) == 0) {
                print '<tr>';
                print '<th>' . dechex($num) . '</th><th>' . $num . '</th>';
            }
            echo '<td>' . chr($twoByte) . chr($twoByte2) . '&nbsp;</td>';
            if (bcmod($num , 16) == 15 ) {
                print '</tr>';
            }
            $twoByte2++;
            $num++;
        }
        $twoByte++;
    }
    // 3バイト文字出力
    $threeByte = 224;
    while ( $threeByte < 240) {
        if ( $threeByte == 224 ) {
            $threeByte2 = 160;
        } else {
            $threeByte2 = 128;
        }
        while ( $threeByte2 < 192) {
            $threeByte3 = 128;
            while ( $threeByte3 < 192) {
                if ( bcmod($num , 16) == 0) {
                    print '<tr>';
                    print '<th>' . dechex($num) . '</th><th>' . $num . '</th>';
                }
                echo '<td>' . chr($threeByte) . chr($threeByte2) . chr($threeByte3) . '&nbsp;</td>';
                if (bcmod($num , 16) == 15 ) {
                    print '</tr>';
                }
                $threeByte3++;
                $num++;
            }
            $threeByte2++;
        }
        $threeByte++;
    }
    ?>
    </table>
  </body>
</html>

chrという関数が、数値から1バイトの文字列を出力する関数です。
2バイト、3バイト文字は関数をつなげることで出力できます。
2バイト目、3バイト目の仕様は下記のサイトをご参考ください。

https://ja.wikipedia.org/wiki/UTF-8