プロが教える店舗&オフィスのセキュリティ対策術

まず、【あ】という文字をUTF-16というエンコーディング方式で
バイト配列化すると
[0] => 66

[1] => 48

というバイト値が帰ってきます。
これはとりあえずC#で処理を行いました。
Encoding encode = Encoding.GetEncoding("UTF-16");
String value = "あ";
Byte [] byteData = encode.GetBytes(value);

それでは【あ】という文字の文字集合のコードポイントは右記となります。【0x3042】
これは、出力された 66、48という数値を16進数に変換すると
66=>42
48=>30
となり、リトルエンディアン環境で 0x4230という16進数であらわされるようです。
この点も不思議なのですが今回知りたいのはUTF-16でエンコーディングされた
バイト配列はこのように文字集合のコードポイントに復帰?させることができますよね?

ではUTF-8でエンコーディングされた【あ】という文字の
バイト配列
[0] => 227
[1] => 129
[2] => 130
というバイト配列を上記のような文字集合のコードポイントの値へ復帰させることは
できるのでしょうか?
それに伴いUTF-8でつかわれる文字集合もUTF-16と同じコードポイントなのでしょうか?

上記
1.UTF-8でエンコーディングされた文字列をバイト配列にした場合
元の文字集合に戻せるのか?戻せるならばその戻し方は?
2.UTF-8がさす文字集合とUTF-16がさす文字集合はおなじものですよね?

上記二点よろしくご教授ください。

A 回答 (5件)

> つまりだいたいの日本語は


> ユニコードのコードポイント値を10進数にすると0~65535までの数値でおさめている訳ですよね?

日本語,というよりも,Unicode制定当時の主要な文字コードで使われていた文字集合は,BMPに含まれます。
なので,日本語に限らず,ヨーロッパ各国語等 (ISO-8859ファミリ) や韓国語,中国語なども0~65535におさまっています。


> BMPというのはUnicodeの初期における定義ですよね。

はい。Unicodeの,であって,ISO/IEC 10646-1ではないです。
# ISO/IEC 10646は全く別ですし,ISO/IEC 10646-1は31bit系。


> ん?ていうことは、常に16bit(2バイト固定長?といういのでしょうか)で符号化された規格
> UTF-16というのは非常に使えない?(用途としては?)エンコード方式となるのですかね?

今のところ,内部エンコーディングとしてはUTF-16が一番効率がよいと思います。
後で書きますが,Unicode自体「1文字が複数コードポイントからなる」ことのある文字集合であり,
その上でBMP外の文字が使われることが少なく,「1文字が複数コードポイントからなる」ことも少ないためです。
# 1文字に32bit使ってもクライアント側ならなんとかなるかもしれませんが……。


UTF-16は基本多言語面 (第0面) だけでなく,第16面までを16bit単位の符号化の中で使うための規格です。
そのために,BMPのU+D800 - U+DFFFは未定義領域になっています。
この領域の前半分,上位サロゲート (0xD800 - 0xDBFF) と後半分,下位サロゲート (0xDC00 - 0xDFFF) の2文字を組にすることによってBMP外の文字を使います。
これをサロゲートペアと呼びます。
UTF-16でのエンコードでは,上位サロゲートが先に,下位サロゲートが後にきます。
このサロゲートペアは,Unicodeのコードポイント「(上位サロゲート - 0xD800) * 1024 + (下位サロゲート - 0xDC00) + 0x10000」にマッピングされます。
なお,UTF-8は「コードポイントを符号化する」のであって,「UTF-16を別表現にする」のではないため,BMP外の文字は4バイトに直接符号化され,3バイト×2に符号化してはいけません。

また,そもそも「結合用文字」などがあるため,サロゲートペアを使わなくても1文字は固定長ではありません。
例えば,OS XのHFS+ではUnicode正規化D (NFD)であるため,「が」という文字は,
・WindowsではU+304C (が) が
・OS XではU+304B (か) U+3099 (結合用濁点) が
となります。結合用文字はUnicode 1.0の頃から存在するため,元々「1文字」と「1コードポイント」は別物でした。
結合用文字はコードポイントが振られているので,UTF-8であっても結合用文字に独立した符号が割り当てられています (NFDで正規化した「が」は6バイト)。


> でもJAVAもC#も内部的?にはUTF-16だとかかいていたような・・。

はい。

なので,サロゲートペアや結合用文字を使うとlength/Lengthと文字数が一致しなくなります。
C#では,System.Globalization.TextInfoクラスなどを使って文字数や各文字情報を取得します。
http://msdn.microsoft.com/ja-jp/library/system.g …
Javaでは,java.text.BreakIteratorクラスを使うそうです。
http://docs.oracle.com/javase/7/docs/api/java/te …


> 文字列と文字列の比較の比較ってこの符号化したバイト列で判断しているんでしょうか?

通常は内部で保持している各文字の比較です。
文字の比較というのもややこしく,そのために先に出てきたUnicode正規化というものがあります。
XMLなどでは,「文書はUnicode正規化C (NFC) で書かれていること」が要求されていたりします。
# NFCでは正規分解後合成するため,結合用濁点付き平仮名は,対応する文字が定義されているならば単一のコードポイントになる


> うーんでも 数値の1と文字列としての1だと矛盾するのか・・?

これはPHPでの話でしょうか。
PHPの==による比較は,「数値」との比較は数値での比較になります。
1 == "1"は,1 == 1となります。
# 1 == "1a"も,1 == 1だったりしますが……。

この回答への補足

何度も何度もありがとうございます。


簡単にまとめると、
Unicodeという文字集合は
あらゆる文字を 16ビットで表現しようとする規格で、当初はそれで問題なかった。

当初のUnicode規格はまさに16ビット分 256 ×256 =65536通りを表現していた。
(※これは基盤他言語面 BMPとなる箇所ですね)
ただ後のサロゲートペアとなる部分は未定義だった。

その後、世界中の文字を文字集合に含める際に、65536通りの中に含めるには明らかに足りない。
そのときにとられた方法が

> (0xD800 - 0xDBFF) と後半分,下位サロゲート (0xDC00 - 0xDFFF) の
>2文字を組にすることによってBMP外の文字を使います。
>これをサロゲートペアと呼びます。

に当初運良く?未定義だった箇所を追加の文字集合として定義するのに使用したわけですね。

この際に、わざわざサロゲートペアを作った理由は

確認1. => 既に定義されていた BMP面の規格を崩すわけにはいかなかったからという捉え方でよいでしょうか?

でこのサロゲートペアの符号化の方法ですが例えば

※てっとりばやかったのでPHPでやります。
<?php
$value = "マルチバイト文字列";
$value ="

補足日時:2012/11/19 21:40
    • good
    • 0

> どのような法則でこのような計算になっているのか・・・・。



ANo.1に書かれている定義を見れば一目瞭然、比較的理解しやすい変換規則だと思うのですが、もしかして2進数とかビット演算にあまり馴染みがないのでしょうか。

UTF-8からUnicodeコードポイントへの変換:
(1) 1バイト目を取ってくる (Xとする)
(2) Xの最上位ビットが0ならば、Xを16ビットに拡張した値がUnicodeコードポイント → 終了
(3) Xの上位3ビットが110ならば
(3a) 2バイト目を取ってくる (Yとする)
(3b) Yの上位2ビットが10ならば、Xの下位5ビットとYの下位6ビットを合わせて16ビットに拡張した値がUnicodeコードポイント → 終了
(3c) Yの上位2ビットが10でないならばエラー → 終了
(4) Xの上位4ビットが1110ならば
(4a) 2バイト目と3バイト目を取ってくる (YとZとする)
(4b) YおよびZの上位2ビットが10ならば、Xの下位4ビットとYの下位6ビットとZの下位6ビットを合わせた値がUnicodeコードポイント → 終了
(4c) YまたはZの上位2ビットが10でないならばエラー → 終了
(5) Xの上位5ビットが11110ならば … 以下略
(6) Xの上位ビットが(2)~(5)の条件を満たさないならばエラー → 終了

この回答への補足

おせわをかけます。

>ANo.1に書かれている定義を見れば一目瞭然、比較的理解しやすい変換規則だと思うのですが、もしかし
>て2進数とかビット演算にあまり馴染みがないのでしょうか。

ええ。実はそうなのです。
コンピュータを専門で学んだことないので内部的挙動はかなり疎いです。

>(1) 1バイト目を取ってくる (Xとする)
>(2) Xの最上位ビットが0ならば、Xを16ビットに拡張した値がUnicodeコードポイント → 終了

これは1バイト目 が 00000000 ~ 01111111
つまり1バイト目 が 0 ~ 127 まで

これはたとえば特定の文字列(一文字分)を0~255の1バイトの範囲の数値に置き換えたとき
(C#やJAVAならGetBytes()系メソッド?PHPやRubyならunpack系メソッドで行ったときに取得できる0~255間の
値のことですよね?)
0~127間の値であればアスキー文字だからその時点で終了。


どうも二進数になれていないのであれですが
これってつまり特定の文字列(一文字分)から取得した最初の1バイト分の数値が
0~127だったら1バイト(0000 0000 ~0111 1111)
192~223までだったら2バイト文字(1100 0000 ~ 1101 1111)
224~239までだったら一般的な日本語の3バイト文字(1110 0000 ~ 11101111)
240~247までだったら日本語を扱う上ではあまりみかけない?4バイト文字(1111 0000 ~1111 0111)

というようなとらえかたでよいのでしょうかね?
これをみるに各バイト毎に256通り全てがわりあてられているわけではないのですね。

なんとなーくUTF-8がユニコード上のコードポイントをどのように符号化しているのか、
わかりました。

なるほどー・・・。

ありがとうございます、たすかりました。

補足日時:2012/11/18 00:57
    • good
    • 0
この回答へのお礼

あー、そういえば今まで考えていませんでしたが

文字列と文字列の比較の比較ってこの符号化したバイト列で判断しているんでしょうか?

うーんでも 数値の1と文字列としての1だと矛盾するのか・・?
そのときは型と型をみるのでしょうかね?

お礼日時:2012/11/18 01:01

#1補足1.は既に回答が付いたので……。




> つまりたいていの日本語はU+0800 - U+FFFFのコードポイントの範囲に収まっているということですかね。

JIS X 0208や0212の範囲のひらがな・カタカナ・漢字は全てその範囲に収まっています。
さらに,「機種依存文字」と呼ばれることのある,Windows-31jで追加された漢字もその範囲に含まれます。

JIS X 0213の文字の一部は基本多言語面 (BMP : U+0000 - U+FFFF) に含まれていないため,その一部の文字である「

この回答への補足

なるほど・・・・。ユニコードの概要はなんとなーくつかめたような気がします。

がこのTUF-8の符号化の計算方法がまったくわかりませんね・・・。
これはもうこういう規則だと理解するしか無いんですかね?
どのような法則でこのような計算になっているのか・・・・。

補足日時:2012/11/17 01:19
    • good
    • 0
この回答へのお礼

>JIS X 0208や0212の範囲のひらがな・カタカナ・漢字は全てその範囲に収まっています。
>さらに,「機種依存文字」と呼ばれることのある,Windows-31jで追加された漢字も
>その範囲に含まれます。

つまりだいたいの日本語は
ユニコードのコードポイント値を10進数にすると0~65535までの数値でおさめている訳ですよね?

>JIS X 0213の文字の一部は基本多言語面 (BMP : U+0000 - U+FFFF)
>に含まれていないため,その一部の文字である「

お礼日時:2012/11/18 01:25

> 1. 227 129 130 という並びは ユニコードの文字集合コードポイント値【0x3042】


> という値を指し示す・・・というか同義である
> と解釈できるのでしょうか?

10進→2進で表すと
227→11100011
129→10000001
130→10000010

先頭バイトが1110で始まっているのでこれは3バイトで表された文字と判断できます。
各バイトから必要なビットを順に抜き出すと
0011 000001 000010
この16ビット2進数を16進数に直すと
0x3042
となります。

この回答への補足

ご解説ありがとうございます。

>先頭バイトが1110で始まっているのでこれは3バイトで表された文字と判断できます。
>各バイトから必要なビットを順に抜き出すと
>0011 000001 000010
上記解説部分は以下URLの
>http://lab.moyo.biz/translations/rfc/rfc2044-ja. …

【2. UTF-8 定義】
の項目の方法のことでしょうか?

補足日時:2012/11/16 10:36
    • good
    • 0

えーっと,エンコーディングと文字集合は関係しませんよ。


例えば,Shift_JISで書かれたHTML 4.01の文書は,
・エンコーディング:Shift_JIS
・文字集合:ISO 10646-1 (≒Unicode)
です。

さて,UTF-8とUnicodeコードポイントの変換ですが,定義があります。
・U+0000 - U+007F (0000-0000 0abc-defg)
そのまま1バイト値になる (0abc-defg)
・U+0080 - U+07FF (0000-0abc defg-hijk)
2バイトになる (110a-bcde 10fg-hijk)
・U+0800 - U+FFFF (abcd-efgh ijkl-mnop) ただしサロゲートペア部を除く
3バイトになる (1110-abcd 10ef-ghij 10kl-mnop)
・U+10000 - U+10FFFF (000a-bcde fghi-jklm nopq-rstu)
4バイトになる (1111-0abc 10de-fghi 10jk-lmno 10pq-rstu)
昔はこの先に5バイト,6バイトもありましたが現在は廃止されています。

ref)
http://tools.ietf.org/html/rfc3629
http://ja.wikipedia.org/wiki/UTF-8

この回答への補足

いつも、ご教授ありがとうございます。

さて、解答いただいた内容が今ひとつ理解できないので
ちょっと質問の仕方をかえて【あ】という文字のBytes配列
[0] => 227
[1] => 129
[2] => 130

つまり

1. 227 129 130 という並びは ユニコードの文字集合コードポイント値【0x3042】
という値を指し示す・・・というか同義である

と解釈できるのでしょうか?

また
>・U+0800 - U+FFFF (abcd-efgh ijkl-mnop) ただしサロゲートペア部を除く
>3バイトになる (1110-abcd 10ef-ghij 10kl-mnop)
とありますね。

これはすなわち、

2. UTF-8で ひらがな漢字カタカタをバイト配列にすると
3バイト分を容量としてとりますよね?
つまりたいていの日本語はU+0800 - U+FFFFのコードポイントの範囲に収まっているということですかね。

いまいち自分が、どのへんでつまずいているのもわかってないのですが・・・・。

補足日時:2012/11/15 20:18
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!