A Day In The Boy's Life -116ページ目

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

自分もアフィリエイトしているので批判的な意見っていえる立場に無いですけど、アフィリエイトという言葉によって、自分たちは巨大な広告ネットワークを作ることに加担させられているのではなかろうか、と思ったりします。


例えばAmazonとか色んなブログにつけられている広告を見ると、特定のサイトだけに広告を載せる以上に情報の爆発的広がりがあるな、と感じたりします。

もちろんアフィリエイトはそれをやっている人にも恩恵があるシステムですけど、その恩恵を本当に受けているのってごく一部の人だけです。

その言葉に乗せられてせっせと広告をブログに貼り付ける作業は、単に宣伝主の情報を広めているという以上のものを生み出してしまっている、と思ったり。



アフィリエイトのメリットを受ける人


実際にアフィリエイトをやっていて、その恩恵に与れる人っていうのは、ごくごくわずかな人なのだろうな、と思います。

以前にどこかで、自分のブログでどれだけアフィリエイトで稼げているか調査した結果が載ってましたけど、中には数十万とかの金額の人もいたようですが、それはごく一部の人でした。

やはりアクセス数が数百程度のブログを運営している人では、ブログを書くコスト以上に見合ったものをそこからもらえることは厳しいと思います。(書く時間でバイトでもした方がましだったりするし)


まぁ、多くはブログを書くことを生活の生業にしているというわけではないでしょうし、お金では得られないこともあったりしますので、それは二の次だと考えている人も多いと思います。

ただ、そんな考えでもちょっとの足しになればいいやと、広告のリンクを貼り付けていくと、その膨大な量がネットに流れていったりします。


そして過去の多くの記事は見返されることが無いので、そのまま放置されることがほとんどではないでしょうか。

それは、すでにアフィリエイト運営会社との契約締結を打ち切ったものも含まれるでしょうけど、テキストリンクの場合はそのプロモーションの情報がいつまでもそのブログ記事の中に含まれたりもするわけです。

アフィリエイトの効果がほとんど無いブログであったとしても、そこを訪れた人はその情報を目にすることはあります。

それが直接的に購買に結びつかなくても、その情報を目に触れさせるという何らかの印象を少なからず与えることもできるでしょう。


また、例え年に数回自分のブログを通して誰か買ってくれた人がいたとしても、一定の金額の売上げが無ければ還元されないシステムもあり、その条件を満たすためにはかなり長い期間、ブログを運営していなければならなかったりします。

途中で諦めたり、いつの間にか更新を止めたブログでは、その恩恵を最後まで受けることなく終焉を迎えるということも多いでしょう。


その他にも、自分のブログを通して広告が見られたとしても、直接的に購買に結び付けようとせず、その情報を一時的にストックして、後日検索したり直接ショップで見てみるということもあります。

小さなメディアが大きなメディアに飲み込まれ、そこでの恩恵もまた別の場所へと移ってしまうということもよくあることだと感じたりします。



アフィリエイトのロングテール


ブログがそのまま放置されて更新されず、ネットの海に漂うことになったとしても、検索エンジンを通してそこを訪れるということは少なからずあったりします。

同様にそこにくっ付いている広告もまた、訪問者の目に留まることになるわけです。


それを通して購買に結びつく確率というのはものすごく低いと思います。

記事と広告がマッチしていないと興味を持たないでしょうし、検索して尋ねてきた人は、何らかの疑問や問題があったから探しているわけであって、そこで何かを買おうと考えているわけではなかったりしますからね。

ただ、そんな1日に1度見られるかどうかというようなエントリでさえ、膨大なブログの数と記事からすれば、ものすごい量の広告になりえるわけです。


日の光を浴びているブログからだけでなく、今はその役目を終えたブログでさえ、その広告の一端を担っているとなると、アフィリエイトのロングテールや~、なんて思ったりもします。

古い情報も含めてネットでは膨大な広告が流れている。

それは、過去に自分たちが「今の恩恵」を受けるためにやったことが、過ぎ去り記憶からなくなったとしても、その広告だけはきちんと働いてたりするわけです。


アフィリエイトの恩恵を受けることなく、その言葉によって作られた膨大な量の広告ができていることと、ブログ主の意思にかかわらず、その広告だけが活きつづけるというところが、ネットの中で膨大な広告ネットワークを形成しているのかなと。

今思えば、アフィリエイトって言葉はなんだがアフィリエイトを運営する会社にとって随分と都合のいい言葉であるんだなぁ、と思ったりもします。


まぁ、その言葉の誘惑に負けて、多少の足しにでもなればいいやとやってたりもしますけど、裏ではそのメリットを自分が受けるとる以上のものが働いているんだなぁ、と感じたりしました。



自分でもそれを完全に理解しているわけでもなしに、エントリを書いてみるということはよくあります。

間違って指摘されることもあれば、他の人の似たようなエントリを読んで「そういう意見もあるよね」と妙に自分が清々といったことに対して恥ずかしさを覚えることもあります。

しかし、多くは知らない故に書けるというほうが多いような気もします。



本当に知っていることってナンダ・・・?


単に事実を並べて知っていることを書いてみるブログでさえ、本当に知っているという人は少ないでしょう。

多くの知っているという事実は誰かのコピーだったりもしますし、知らない故にオリジナルが生まれるということもあります。

知っているという定義もまたあいまいなもので、人によって意見が異なるものもあれば、深追いするときりが無いものもあり、その表面上の知識を学んだだけで知っているという人もいるでしょう。


これは紛れも無い事実だ!といって、あるシステムの真実を描いたとしても広い視点で見れば、そのシステム自体が正しいのかどうか、というところの議論が持ち上がったりもします。

結局のところ何が正しいかなんて誰もわからないままアウトプットしていくのかな、と思ったりもします。


知らないことをアウトプットしてみるというのは、間違いも多く含むでしょうけど自己の成長の過程で出てくるオリジナルの考えだったりもします。

その事実を正しく理解してないとブログかいちゃダメ、なんてルール作るとほとんどの人は何もかけなくなりますし、ネット上での議論ってそんなに活発に起きなくなり、ネット自体の有効性って薄れることになるのではなかろうかと思ったりします。


知らないから自分の意見を書いてみる。

それをdisられて得る新たな意見というものもあるでしょうし、知らないが故にあたかもそれが真理であるかのように振舞えて書くこともできたりします。

知らないことについて、あいまいな意見ばかりを述べていたら人は信じたり受け入れられたりしてくれません。

だまされても信じ込まされた方が理解しやすいですし、何が正しいのか自分で考えようなんてことを言うと、答えは永遠に見つかりませんし、人間どこかで線を引きたいものですから、勘違いでもそう悟ってしまったりもします。


この辺は、ネット上でのノイズを生み出す原因にもなったりしますが、結局のところ失敗を繰り返すことが真実に近づく一番の近道であるのかなと感じたりしますので、致し方ないという思いもあったりします。



今知っていることをアウトプットしてみるというテスト


アウトプットの一環として活用するブログ 」にも書きましたが、知らないことを定期的にアウトプットしてみるのは、2つのことで意味があるのかな、と思っています。


1. 今知っていることを自分の頭の中で体系化してアウトプットできる

2. そのアウトプットの正否を他人の意見や事実と照らし合わせることができる


「知る」というのは自分の言葉で説明できるようになること、なんていわれた事ありますけど、とりあえずアウトプットを出しながら考えてみることが一番手っ取り早い方法だったりします。

そして、アウトプットを出すということは、答えあわせをできるということでもあります。

ブログであれば、コメントで間違いを指摘されたりもしますし、他人のエントリを読んでその考えの相違を知ることもできます。


知らないということは、イコール無知であるということ思われがちですけど、例えそれが1%であれ、ここまでは知っているんだということにもなると思うわけです。

だからそれは次のステップにつなげるためのマイルストンの1つだと考えれば、知らないことでも書くことができるのかなと。



まとめ


まぁ、そんな細かいことなんて気にしないでブログを書いてみるというのが一番楽な方法かもしれません。

知らないこと、知っていることなんて考え出すと気後れしたりもしますからね。

知らない故に書ける考えや勢いというものもあるかと思ったりします。


知らないということに臆することなく、知るために知らないことをブログに書いてみるということも意味があるのかな、と感じたりします。




UTF-8の冗長なエンコードとは何で、なんでそれがセキュリティ的に危ないのか?を文字コード知識レヴェル3くらいの凡プログラマが考えてみる @ それ図解で。・・・tohokuaikiのチラシの裏


これを読んで、ちゃんと疑問に持ったことを検証したりするのって凄いなとか、自分は文字コードってかなりなんとなくでしかわかっていないな、と思いこれがレベル3であればそれを埋めるための知識をちゃんと理解しようと勉強しつつエントリをまとめてみました。



はじめに


ってことで、文字コードを理解するに当たって現状はというと、


・ 文字コードってメジャーどころだとEUC-JPとかShift-JISとかUTF-8とか色々あるよね

・ その当たりのコード変換すると理由はよくわからないけど文字化けすることあるよね

・ 文字コード表ってよく見るけどマトリックス表に漢字とかが埋められてて、それが文字コードごとに違うんだよね


ってぐらいのレベル(正直ひどい)

まぁ、それでも困らずエンジニアをやってこれたところもあるのですが、やっぱり基礎のところをちゃんと抑えておきたいなと。

アプリケーションのレイヤだけしか気にしていないとあまりこういった文字コード周りも真剣に触れる機会も無かったりします。

どこで対応すべきかって議論はありますけど、基本ミドルでやってくれたらいいなぁ、なんて思いもあったりしますからね。


ゴールとしては、先に紹介した記事につながる感じ。

ここでの知識を持ってあのエントリを読めばやろうとしていたことがわかりましたよ、ってところを目標にしています。


ちなみに、文字コード表なんかを見ると「0x41」とか16進数や2進数で表現されていることが多く、そのあたりの計算方法などは割愛します。(というか掘り下げだしたら数学の授業になる・・・)


※ 頭に「0x」がつくと16進数という意味



レベル1の文字コード入門: ASCIIコード編


とりあえず文字コードの歴史から・・・、となるとかなり長くなりますし普段そこまで多くの文字コードに触れることも無いので割愛。

ここでは、ASCIIとEUC-JP、Shift-JISの文字コードを中心に書いていきます。

歴史的な背景も含めて勉強するのであれば、下記が参考になるかと思います。


参考: 文字コードの話 @ 伊藤隆幸のホームページ


まずは基本となるASCIIコードから。

ASCIIコードは、American Standard Code for Information Interchangeの略であるというところからもわかるように、英語圏で使用する文字を定義しているコード体系になっています。

ASCIIコード表(@IT用語辞典) を見てみると、キーボードに並んでいる英数字や記号が管理用のテーブルに並んでいることがわかります。


で、このコードが7ビットのコードでできていると。

1バイトは8ビットであるのになんで?ってところがありますが、英語圏では8ビット(256文字)もイラネってところがあって、その半分の7ビット(128文字)分しか使わないようになったようです。(最上位ビットは常に0)


参考: パソコン・ノート (文字コードの話) @ ハングル工房 東京綾瀬


なので、7ビットのコードに128文字分割り当てているわけです。

(実際には制御文字ってのがあって目に見えない文字になり、コンピュータとの通信用に使われたりする文字が含まれています)

例えば、「0x41」は「A」を表すという具合に。


じゃあ、7ビットで文字が表せるんだよね、ってことで試しに出力するPHPスクリプトを作ってみました。

ちなみにASCIIコードの場合は、0x1fまで(32文字分)が制御文字になっていて、出力しても見えないのでそれ以降を出力するようにしています。


<?php

for ($i = 32; $i < 128; $i++) {

    echo $i . "\t" . // 10進で出力
          sprintf("%07s", base_convert($i, 10, 2)) ."\t" . // 2進で出力
          sprintf("%04s", base_convert($i, 10, 16)) . "\t" . // 16進で出力
          "\"" . pack("H*", base_convert($i, 10, 16)) . "\"" . // 該当コードの文字
          "\n";
}

?>

32      0100000 0020    " "
33      0100001 0021    "!"
34      0100010 0022    """
35      0100011 0023    "#"
36      0100100 0024    "$"
37      0100101 0025    "%"
38      0100110 0026    "&"
39      0100111 0027    "'"
40      0101000 0028    "("
41      0101001 0029    ")"
42      0101010 002a    "*"
43      0101011 002b    "+"
44      0101100 002c    ","
45      0101101 002d    "-"
46      0101110 002e    "."
47      0101111 002f    "/"
48      0110000 0030    "0"
49      0110001 0031    "1"
50      0110010 0032    "2"
51      0110011 0033    "3"
52      0110100 0034    "4"
53      0110101 0035    "5"
54      0110110 0036    "6"
55      0110111 0037    "7"
56      0111000 0038    "8"
57      0111001 0039    "9"
58      0111010 003a    ":"
59      0111011 003b    ";"
60      0111100 003c    "<"
61      0111101 003d    "="
62      0111110 003e    ">"
63      0111111 003f    "?"
64      1000000 0040    "@"
65      1000001 0041    "A"
66      1000010 0042    "B"
67      1000011 0043    "C"
68      1000100 0044    "D"
69      1000101 0045    "E"
70      1000110 0046    "F"
71      1000111 0047    "G"
72      1001000 0048    "H"
73      1001001 0049    "I"
74      1001010 004a    "J"
75      1001011 004b    "K"
76      1001100 004c    "L"
77      1001101 004d    "M"
78      1001110 004e    "N"
79      1001111 004f    "O"
80      1010000 0050    "P"
81      1010001 0051    "Q"
82      1010010 0052    "R"
83      1010011 0053    "S"
84      1010100 0054    "T"
85      1010101 0055    "U"
86      1010110 0056    "V"
87      1010111 0057    "W"
88      1011000 0058    "X"
89      1011001 0059    "Y"
90      1011010 005a    "Z"
91      1011011 005b    "["
92      1011100 005c    "\"
93      1011101 005d    "]"
94      1011110 005e    "^"
95      1011111 005f    "_"
96      1100000 0060    "`"
97      1100001 0061    "a"
98      1100010 0062    "b"
99      1100011 0063    "c"
100     1100100 0064    "d"
101     1100101 0065    "e"
102     1100110 0066    "f"
103     1100111 0067    "g"
104     1101000 0068    "h"
105     1101001 0069    "i"
106     1101010 006a    "j"
107     1101011 006b    "k"
108     1101100 006c    "l"
109     1101101 006d    "m"
110     1101110 006e    "n"
111     1101111 006f    "o"
112     1110000 0070    "p"
113     1110001 0071    "q"
114     1110010 0072    "r"
115     1110011 0073    "s"
116     1110100 0074    "t"
117     1110101 0075    "u"
118     1110110 0076    "v"
119     1110111 0077    "w"
120     1111000 0078    "x"
121     1111001 0079    "y"
122     1111010 007a    "z"
123     1111011 007b    "{"
124     1111100 007c    "|"
125     1111101 007d    "}"
126     1111110 007e    "~"
127     1111111 007f    ""

きちんとASCIIコード表になっていることがわかります。

最後の0x7fは何も出力されていませんが、これは「DEL」を表し制御文字になっています。

16進の1バイトデータを復号(目に見える文字に戻すこと)すれば右端の文字に変わるわけです。

逆に普段使われているASCII文字は内部では16進のデータに符号化(デジタルデータに変換すること)されていることがわかります。


予断ですが、PHPスクリプト内で使っているpack() という関数は、バイナリデータを一括りにするための関数です。

オプションに「H*」をつけると、16進数の文字列で上位ニブルが先という意味になります。

ニブルというのは、4ビットをさします。

つまり、上位から4ビットずつバイナリデータを括るという操作になっています。


参考: pack関数とunpack関数の基本を理解する @ Chaichan-World !WEB相談室


ビット数の並びによって文字のデータを作り出しているわけですね。

文字コード表のイメージがなんとなくつかめたでしょうか?


- ここまででわかったこと

・ 文字コードは決められたデータ(ビット長)に符号化されており、復号する処理で目に見える文字となる



レベル1の文字コード入門: Shift-JISコード編


で、次にShift-JISのコード表を出力してみます。

Shift-JISの場合、Shift-JISコード表 を見ればわかるように、1バイトコードと2バイトコードに分かれており、先ほど書いたように0x00から0x7fまではASCIIコードと同じ配列になっています。

それに加え、ASCIIコードでは余っていた1バイトコードの残りの部分に半角カナの文字を定義しています。


一方で、2バイトコードは上位1バイトが0x81から0x9f、0xe0から0xeeで始まる部分に、様々な漢字や全角記号が定義されています。

それらの部分は、1バイトコード部分を見てみると未定義になっていることがわかります。

まとめるとこんな感じ


1バイト領域 0x00~0x1f、0x7f 制御文字
0x20~0x7e ASCII文字
2バイト領域 上位1バイト 0x81~0x9f、0xe0~0xef 全角文字
下位1バイト 0x40~0x7e、0x80~0xfc

なので、Shift-JISコードを1バイトを取得してみて「0x81から0x9f」または、「0xe0から0xee」で始まっていれば全角の領域ということに、それ以外は半角文字(半角カナを含む)というロジックがあるわけです。


参考: 第5回■注目される文字コードのセキュリティ問題(P.2) / @ ITpro


で、同じように文字コードを出力してみます。


<?php

// 漢字コード出力
// 上位1バイト目が0x81(129)から0xef(240)まで
for ($i = 129; $i < 240; $i++) {

// 上位1バイト目の0xa0(160)から0xdf(225)までは対象コードなし
    if ($i > 159 && $i < 224) {
        continue;
    }

// 下位1バイト目が0x40(64)から0xfc(252)まで
    for ($j = 64; $j < 253; $j++) {
         echo sprintf("%02s", base_convert($i, 10, 16)) . // 上位1バイト目(区)
              sprintf("%02s", base_convert($j, 10, 16)) . "\t" . // 下位1バイト目(点)
              "\"" . pack("H*", base_convert($i, 10, 16) . base_convert($j, 10, 16)) . "\"" . // 出力文字
              "\n";
    }
}

// 半角かなコード出力
// 上位1バイト目の上位4ビットが0xa(10)から0xd(13)まで
for ($k = 161; $k <= 223; $k++) {

    echo sprintf("%02s", base_convert($k, 10, 16)) ."\t" . // 2進
         "\"" . pack("H*", base_convert($k, 10, 16)) . "\"" . // 出力文字
         "\n";
}

?>


8140    " "
8141    "、"
8142    "。"
8143    ","
8144    "."
8145    "・"
8146    ":"
8147    ";"
8148    "?"
8149    "!"
814a    "゛"
814b    "゜"
814c    "´"
814d    "`"
814e    "¨"
814f    "^"
8150    " ̄"
8151    "_"
8152    "ヽ"
8153    "ヾ"
8154    "ゝ"
8155    "ゞ"
8156    "〃"
8157    "仝"
8158    "々"
8159    "〆"
815a    "〇"
815b    "ー"
815c    "―"
815d    "‐"
815e    "/"
815f    "\"
8160    "~"
8161    "∥"

-snip-

a1      "。"
a2      "「"
a3      "」"
a4      "、"
a5      "・"
a6      "ヲ"
a7      "ァ"
a8      "ィ"
a9      "ゥ"
aa      "ェ"
ab      "ォ"
ac      "ャ"
ad      "ュ"
ae      "ョ"
af      "ッ"
b0      "ー"
b1      "ア"
b2      "イ"
b3      "ウ"
b4      "エ"
b5      "オ"
-snip-


上位1バイト目の「0xa1から0xb5」までに半角カナを定義してしまっているが故に、単純に1バイト目が特定の領域以降なら半角だ、というような判断ができません。

なんでそんなことをしたのかは、歴史的経緯があるため割愛。

ただ、ASCIIコードとの互換性を持たせるというところに基本があり、余っているところでうまく日本語を使えるようにしましょうや、というところがあるのはわかったりします。


ちなみに、文字コード表を見るとよく区とか点とか書いていますが、2バイトのうちの先行バイトを区、後続バイトを点と読んでいます。

これは、EUC-JPでも同様です。



レベル1の文字コード入門 EUC-JPコード編


次に、EUC-JPのコードです。

こちらも、EUC-JPのコード表 を見ればわかるように、1バイト目はASCIIコードとあわせて定義されています。

Shift-JISとの違いは、1バイト目はASCIIコードとまったく同じで半角カナを別の領域(2バイトコード目)に定義されていることです。


1バイト目で空きになっている「0xa1から0xfe」が全角文字用に割り当てられています。


1バイト領域 0x00~0x1f、0x7f 制御文字
0x20~0x7e ASCII文字
2バイト領域 上位1バイト 0xa1~0xfe 全角文字
下位1バイト 0xa1~0xfe

こちらの方がShift-JISに比べてすっきりしてわかりやすいですよね。

単純化すると1バイト目が「0xa1」以降であれば全角文字なんだな、ということが判断できるので。


参考: 第5回■注目される文字コードのセキュリティ問題(P.3) @ ITPro


ただし、EUC-JPの場合は、半角カナが2バイトで表されており、1バイト目が「0x8e」の領域に定義されています。

1バイト目が「0x8e」で始まる場合は、半角カナということになっており、Shift-JISと大きく異なります。


<?php

// 漢字コード出力
// 上位1バイト目が0xa1(161)から0xfe(255)まで
for ($i = 161; $i < 255; $i++) {

// 下位1バイト目が0xa1(255)から0xfeまで
    for ($j = 161; $j < 255; $j++) {
         echo sprintf("%02s", base_convert($i, 10, 16)) . // 上位1バイト目(区)
              sprintf("%02s", base_convert($j, 10, 16)) . "\t" . // 下位1バイト目(点)
              "\"" . pack("H*", base_convert($i, 10, 16) . base_convert($j, 10, 16)) . "\"" . // 出力文字
              "\n";
    }
}

// 半角かなコード出力
// 下位1バイト目が0xa1(161)から0xdef(224)まで
for ($k = 161; $k < 224; $k++) {
    echo "8e" . // 上位1バイト目(区)は8eで固定
         sprintf("%02s", base_convert($k, 10, 16)) . "\t" . // 下位1バイト目(点)
         "\"" . pack("H*", "8e" . base_convert($k, 10, 16)) . "\"" . // 出力文字
         "\n";
}

?>

a1a1    " "
a1a2    "、"
a1a3    "。"
a1a4    ","
a1a5    "."
a1a6    "・"
a1a7    ":"
a1a8    ";"
a1a9    "?"
a1aa    "!"
a1ab    "゛"
a1ac    "゜"
a1ad    "´"
a1ae    "`"
a1af    "¨"
a1b0    "^"
a1b1    " ̄"
a1b2    "_"
a1b3    "ヽ"
a1b4    "ヾ"
a1b5    "ゝ"
a1b6    "ゞ"
a1b7    "〃"
a1b8    "仝"
a1b9    "々"
a1ba    "〆"
a1bb    "〇"
a1bc    "ー"
a1bd    "―"
a1be    "‐"
a1bf    "/"
a1c0    "\"
a1c1    "~"
a1c2    "∥"

-snip-

8ea1    "。"
8ea2    "「"
8ea3    "」"
8ea4    "、"
8ea5    "・"
8ea6    "ヲ"
8ea7    "ァ"
8ea8    "ィ"
8ea9    "ゥ"
8eaa    "ェ"
8eab    "ォ"
8eac    "ャ"
8ead    "ュ"
8eae    "ョ"
8eaf    "ッ"
8eb0    "ー"
8eb1    "ア"
8eb2    "イ"
8eb3    "ウ"
8eb4    "エ"
8eb5    "オ"
-snip-


出力する文字は同じでも符号化している体系がShift-JISとはまったく異なっていることがわかります。

1バイトコードのと2バイトコードがはっきり分かれているため、Shift-JISよりはわかりやすいですね。


- ここまででわかったこと

・ 文字コードを符号化する方式が文字コードによって異なっている

・ 文字コードの出力時に行われる復号にはロジックが存在している



レベル2の文字コード入門: 文字化けの原因とUTF-8への道


ここまでの話で文字コードって単純にビットの並びを割り当てただけではなく、復号するのに面倒なことしてるんだな、ということがわかりました。

この複雑さが文字化けを起こしたり、セキュリティの問題を引き起こすことになっています。


例えば、先ほどのITproの記事の中であった、EUC-JPの文字化けの例だと


参考: 第5回■注目される文字コードのセキュリティ問題(P.3) @ ITPro


<?php

// 「EMC」の16進表現
$bin = "a3c5a3cda3c3";

$str = pack("H*", $bin);

echo $str;

?>

$ php sample.php
EMC


正しくは、上記のようになりますが、変数$strが何らかの理由で改ざんされたり上手く読み取れず上位1バイトが抜け落ちると


$ php sample.php
釘唯築

というような表示になります。

「0xc5a3」が「釘」に、「0xcda3」が「唯」と符号化されてしまうわけですね。

最後に「築」が出てますが、表示上は「築」に見えて「築」ではない文字です。


さらには、文字コードの始めに別のデータを付け加えることによって待った区別の文字に変更することもできたりします。


<?php

// 「EMC」の16進表現
$bin = "a3c5a3cda3c3";

$bin = "b1" . $bin;

$str = pack("H*", $bin);

echo $str;

?>

$ php moji.php
隠釘唯築

最初と何の関係も無い文字が出てくるわけですね。


で、この誤認するってところがUTF-8の問題にもつながっていきます。

UTF-8は、Shift-JISやEUC-JPなどに比べて符号化のロジックがもう少し複雑です。


1バイト表現 0xxxxxxx (0x00-0x7f)
2バイト表現 110xxxxx 10xxxxxx (0xc0-0xdf) (0x80-0xbf)
3バイト表現 1110xxxx 10xxxxxx 10xxxxxx (0xe0-0xef) (0x80-0xbf) (0x80-0xbf)


符号化のロジックとしては


・ 1バイトは、ASCIIコードと同様

・ 2バイトは、上位1バイトの最初が「110」で始まり、下位1バイトの最初を「10」で始める

・ 3バイトは、1バイト目の最初が「1110」で始まり、以降のバイトの最初を「10」で始める


となってます。(なんかややこしいですが)

ただ、さっきの表の一番右に取りうる16進数を列挙していますが、これを2進数に変換したらきちんと、上記のような符号化に当てはまるようになってるんですね・・・。


<?php

$bin = "e38182";

echo base_convert($bin, 16, 2) . "\t" . pack("H*", $bin);

?>

111000111000000110000010        あ

3バイト表現なので、1バイト目が「110」から始まり、以降バイトの最初が「10」から始まっている。
ただ、あくまで最初の「110」や以降のバイトの最初の「10」というのは、2または3バイトの文字であるということを表すためのフラグのようなものであって、文字としての意味は持っていないようです。

つまりは、


11100011 10000001 10000010


のように太字だけのところで文字が表現されていると。

なので、下記の記事にあるように「/」に複数の表現方法が生まれるというわけですね。


参考: 本当は怖い文字コードの話 @ gihyo.jp … 技術評論社



どうでしょう。

レベル3へつながったでしょうか・・・。