UTF-16を生のUnicodeコードポイントにデコードするUnicodeライブラリの一部があります。ただし、期待どおりに機能していません。
コードの関連部分は次のとおりです(UTF-8と文字列操作のものを省略):
typedef struct string {
unsigned long length;
unsigned *data;
} string;
string *upush(string *s, unsigned c) {
if (!s->length) s->data = (unsigned *) malloc((s->length = 1) * sizeof(unsigned));
else s->data = (unsigned *) realloc(s->data, ++s->length * sizeof(unsigned));
s->data[s->length - 1] = c;
return s;
}
typedef struct string16 {
unsigned long length;
unsigned short *data;
} string16;
string u16tou(string16 old) {
unsigned long i, cur = 0, need = 0;
string new;
new.length = 0;
for (i = 0; i < old.length; i++)
if (old.data[i] < 0xd800 || old.data[i] > 0xdfff) upush(&new, old.data[i]);
else
if (old.data[i] > 0xdbff && !need) {
cur = 0; continue;
} else if (old.data[i] < 0xdc00) {
need = 1;
cur = (old.data[i] & 0x3ff) << 10;
printf("cur 1: %lx\n", cur);
} else if (old.data[i] > 0xdbff) {
cur |= old.data[i] & 0x3ff;
upush(&new, cur);
printf("cur 2: %lx\n", cur);
cur = need = 0;
}
return new;
}
それはどのように機能しますか?
string
は32ビット値を保持する構造体であり、string16
UTF-16のような16ビット値用です。必要に応じupush
て、完全なUnicodeコードポイントをstring
メモリに再割り当てするだけです。
u16tou
私が焦点を当てている部分です。をループし、string16
通常どおり非サロゲート値を渡し、サロゲートペアを完全なコードポイントに変換します。置き忘れた代理人は無視されます。
ペアの最初のサロゲートでは、下位10ビットが左に10ビットシフトされ、最終的なコードポイントの上位10ビットが形成されます。もう一方のサロゲートでは、最下位10ビットがファイナルに追加されてから、文字列に追加されます。
問題?
最高のコードポイントを試してみましょう。
U+10FFFD
、最後の有効なUnicodeコードポイントは0xDBFF 0xDFFD
、UTF-16のようにエンコードされます。それをデコードしてみましょう。
string16 b;
b.length = 2;
b.data = (unsigned short *) malloc(2 * sizeof(unsigned short));
b.data[0] = 0xdbff;
b.data[1] = 0xdffd;
string a = u16tou(b);
puts(utoc(a));
(表示されてutoc
いません。動作していることはわかっています(以下を参照))関数を使用してchar *
、印刷用にUTF-8に変換し直すと、結果U+0FFFFD
ではなく、取得していることが端末で確認できますU+10FFFD
。
電卓で
gcalctoolですべての変換を手動で実行すると、同じ間違った答えが返されます。したがって、私の構文自体は間違っていませんが、アルゴリズムは間違っています。アルゴリズムは私には正しいように思えますが、それでも間違った答えで終わっています。
私は何が間違っているのですか?