Сбой декодера UTF-8 для не-ASCII символов - PullRequest
1 голос
/ 24 сентября 2010

Примечание: если вы следили за моими недавними вопросами, вы увидите, что все они касаются моего упражнения с библиотекой Unicode в C - как одного из моих первых нескольких серьезных проектов в C, у меня много проблем, так что извините, если я задаю слишком много вопросов об одной вещи.

Часть моей библиотеки декодирует кодированные в UTF-8 char указатели в необработанные unsigned кодовые точки. Тем не менее, некоторые самолеты не декодируют правильно. Давайте посмотрим на (соответствующий) код:

typedef struct string {
 unsigned long length;
 unsigned *data;
} string;

// really simple stuff

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;
}

// UTF-8 conversions

string ctou(char *old) {
 unsigned long i, byte = 0, cur = 0;
 string new;
 new.length = 0;
 for (i = 0; old[i]; i++)
  if (old[i] < 0x80) upush(&new, old[i]);
  else if (old[i] < 0xc0)
   if (!byte) {
    byte = cur = 0;
    continue;
   } else {
    cur |= (unsigned)(old[i] & 0x3f) << (6 * (--byte));
    if (!byte) upush(&new, cur), cur = 0;
   }
  else if (old[i] < 0xc2) continue;
  else if (old[i] < 0xe0) {
   cur = (unsigned)(old[i] & 0x1f) << 6;
   byte = 1;
  }
  else if (old[i] < 0xf0) {
   cur = (unsigned)(old[i] & 0xf) << 12;
   byte = 2;
  }
  else if (old[i] < 0xf5) {
   cur = (unsigned)(old[i] & 0x7) << 18;
   byte = 3;
  }
  else continue;
 return new;
}

Все, что upush делает, кстати, помещает кодовую точку в конец string, перераспределяя память по мере необходимости. ctou выполняет декодирование и сохраняет количество байтов, все еще необходимое в последовательности, в byte, а также код выполняемой точки в cur.

Код кажется мне правильным. Давайте попробуем расшифровать U+10ffff, то есть f4 8f bf bd в UTF-8. Делаем это:

long i;
string b = ctou("\xf4\x8f\xbf\xbd");
for (i = 0; i < b.length; i++)
 printf("%z ", b.data[i]);

должен распечатать:

10ffff

но вместо этого он печатает:

fffffff4 ffffff8f ffffffbf ffffffbd

, который в основном является четырьмя байтами UTF-8, с ffffff, прикрепленным перед ним.

Любое руководство о том, что не так в моем коде?

Ответы [ 2 ]

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

Тип char разрешается подписывать, и преобразование в int, а затем в unsigned (что неявно происходит при преобразовании непосредственно в unsigned) показывает ошибку:

#include <stdio.h>

int main() {
  char c = '\xF4';
  int i = c;
  unsigned n = i;
  printf("%X\n", n);
  n = c;
  printf("%X\n", n);
  return 0;
}

Печать:

FFFFFFF4
FFFFFFF4

Вместо этого используйте неподписанный символ.

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

Вы, вероятно, проигнорировали тот факт, что char является типом со знаком на вашей платформе.Всегда используйте:

  • unsigned char, если вы будете читать фактические значения байтов
  • signed char, если вы используете байты в виде маленьких целых чисел со знаком
  • char для абстрактных строк, где вас не интересуют значения, за исключением, возможно, 0. 0. 1012 *

Кстати, ваш код крайне неэффективен.Вместо того, чтобы вызывать realloc снова и снова для каждого символа, почему бы не выделить sizeof(unsigned)*(strlen(old)+1) для начала, а затем уменьшить размер в конце, если он слишком большой?Конечно, это только один из многих недостатков.

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