UTF-16 декодер не работает должным образом - PullRequest
5 голосов
/ 24 сентября 2010

У меня есть часть моей библиотеки Unicode, которая декодирует UTF-16 в необработанные кодовые точки 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 для 16-битных значений, таких как UTF-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 (не показана; я знаю, что она работает (см. Ниже)), чтобы преобразовать ее обратно в UTF-8 char * для печати, я могуПосмотрите в моем терминале, что я получаю U+0FFFFD, а не U+10FFFD в результате.

В калькуляторе

Выполнение всех преобразований вручную в gcalctool приводит к тому же неправильному ответу.Так что сам мой синтаксис не ошибочен, но алгоритм таков.Хотя алгоритм мне кажется правильным, и все же он заканчивается неправильным ответом.

Что я делаю не так?

Ответы [ 2 ]

5 голосов
/ 24 сентября 2010

Необходимо добавить 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 голосов
/ 24 сентября 2010

У вас, похоже, отсутствует смещение 0x10000.

Согласно этой странице WIKI , суррогатные пары UTF-16 сконструированы так:

UTF-16 представляет символы не-BMP (от U + 10000 до U + 10FFFF) с использованием двух кодовых единиц, известных как суррогатная пара.Первые 10000 16 вычитаются из кодовой точки для получения 20-битного значения.Затем это делится на два 10-битных значения, каждое из которых представляется в качестве суррогата, причем наиболее значимая половина помещается в первый суррогат.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...