代码之家  ›  专栏  ›  技术社区  ›  Delan Azabani

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

c
  •  1
  • Delan Azabani  · 技术社区  · 14 年前

    我的部分库解码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 顺便说一句,is把一个代码点推到 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
    

    ffffff 在它前面钉上。

    我的代码有什么问题吗?

    2 回复  |  直到 14 年前
        1
  •  4
  •   Roger Pate Roger Pate    14 年前

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

    印刷品:

    FFFFFFF四
    FFFFFFF四

    改用无符号字符。

        2
  •  2
  •   R.. GitHub STOP HELPING ICE    14 年前

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

    • unsigned char 如果要读取字节的实际值
    • signed char 如果你用字节作为有符号的小整数
    • 对于不关心值的抽象字符串,除了0。

    顺便说一下,你的代码效率非常低。而不是打电话 realloc 每个字符重复,为什么不分配 sizeof(unsigned)*(strlen(old)+1) 一开始,如果太大的话,最后再缩小尺寸?当然,这只是众多低效率问题中的一个。