UTF-16解码器按预期工作

问题描述:

我有一部分Unicode编码解码器将UTF-16解码为原始Unicode编码点。但是,它没有按预期工作。UTF-16解码器按预期工作

下面的代码的相关部分(省略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用于16位值,如UTF-16。所有upush确实是将完整的Unicode代码点添加到string,根据需要重新分配内存。

u16tou是我关注的部分。它循环遍历string16,正常传递非替代值,并将代理对转换为完整的代码点。错位的替代品被忽略。

一对中的第一个替代品的最低10位向左移位10位,导致它形成最终代码点的高10位。另一个代理将最低10位添加到最后,然后将其附加到字符串。

问题?

让我们试试最高的代码点吧?

U+10FFFD最后一个有效的Unicode代码点在UTF-16中被编码为0xDBFF 0xDFFD。让我们尝试解码。

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(未显示;我知道它的工作(见下文))函数将其转换回UTF-8 char *打印,我可以在我的终端是我得到U+0FFFFD看到,因此不是U+10FFFD

在计算器

手工完成所有转换中gcalctool结果是相同的,错误的答案。所以我的语法本身没有错,但算法是。该算法似乎对我来说虽然,但它的结局是错误的答案。

我在做什么错?

解码代理对时,您需要在0x10000上添加;要引用rfc 2781,您缺少的步骤是第5步:

 
    1) If W1 < 0xD800 or W1 > 0xDFFF, the character value U is the value 
     of W1. Terminate. 

    2) Determine if W1 is between 0xD800 and 0xDBFF. If not, the sequence 
     is in error and no valid character can be obtained using W1. 
     Terminate. 

    3) If there is no W2 (that is, the sequence ends with W1), or if W2 
     is not between 0xDC00 and 0xDFFF, the sequence is in error. 
     Terminate. 

    4) Construct a 20-bit unsigned integer U', taking the 10 low-order 
     bits of W1 as its 10 high-order bits and the 10 low-order bits of 
     W2 as its 10 low-order bits. 

    5) Add 0x10000 to U' to obtain the character value U. Terminate. 

即。一个修复将是你的第一次读取后添加一个额外的行:

cur = (old.data[i] & 0x3ff) << 10; 
cur += 0x10000; 
+0

哇,谢谢!添加了一个简单的缺失步骤,并且我的UTF-16解码器正常工作! – 2010-09-24 13:20:28

+0

没问题,很高兴听到它现在起作用。感谢您解决我的错误:) – JosephH 2010-09-24 13:28:33

你似乎缺少的0x10000偏移。

根据this WIKI page,UTF-16代理对构造是这样的:

UTF-16表示非BMP字符 (U + 10000到U + 10FFFF)使用两个 代码单元,被称为代理对。 第一个10000 从 代码点中减去以给出20位值。 然后将其分成两个10位的 值,其中每一个值表示为 代替,其中最显着的 一半放置在第一替代物中。