UTF-8解码器在非ASCII字符上失败

时间:2010-09-24 14:07:29

标签: c

注意:如果你已经按照我最近的问题,你会发现它们都是关于我在C中使用我的Unicode库练习 - 作为我在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,即UTF-8中的f4 8f bf bd。这样做:

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 个答案:

答案 0 :(得分:4)

允许对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

改为使用unsigned char。

答案 1 :(得分:2)

您可能忽略了char是您平台上已签名类型的事实。始终使用:

  • unsigned char如果您要读取字节的实际值
  • signed char如果您使用字节作为小型有符号整数
  • char表示您不关心值的抽象字符串,除非可能为0。

顺便说一下,你的代码效率非常低。为什么不分配realloc来开始,而不是一次性地调用sizeof(unsigned)*(strlen(old)+1),如果它太大,那么减小最后的大小?当然,这只是许多效率低下的原因之一。