1

我有一个嵌入式系统,可以通过 UPNP 获取 UTF-8 编码数据来显示。显示设备具有显示字符的能力。我需要一种将通过 UPNP 接收的 UTF-8 数据转换为 unicode 的方法。显示在 PIC 上,并通过运行 linux 的 UPNP 网桥发送数据。在将其发送到 linux 中的显示板之前,有没有一种简单的方法可以进行转换?

4

3 回答 3

1

要将编码为 UFT-8 的字节数组转换为 Unicode 代码点数组:

诀窍是检测各种编码错误。

#include <limits.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>

typedef struct {
  uint32_t UnicodePoint;  // Accumulated code point
  uint32_t Min;           // Minimum acceptable codepoint
  int i;                  // Index of char/wchar_t remaining
  bool e;                 // Error flag
} UTF_T;

static bool IsSurrogate(unsigned c) {
  return (c >= 0xD800) && (c <= 0xDFFF);
}

// Return true if more bytes needed to complete codepoint
static bool Put8(UTF_T *U, unsigned ch) {
  ch &= 0xFF;
  if (U->i == 0) {
    if (ch <= 0x7F) {
      U->UnicodePoint = ch;
      return false; /* No more needed */
    } else if (ch <= 0xBF) {
      goto fail;
    } else if (ch <= 0xDF) {
      U->Min = 0x80;
      U->UnicodePoint = ch & 0x1F;
      U->i = 1;
    } else if (ch <= 0xEF) {
      U->Min = 0x800;
      U->UnicodePoint = ch & 0x0F;
      U->i = 2;
    } else if (ch <= 0xF7) {
      U->Min = 0x10000;
      U->UnicodePoint = ch & 0x07;
      U->i = 3;
    } else {
      goto fail;
    }
    return true; /* More needed */
  }
  // If expected continuation character missing ...
  if ((ch & (~0x3F)) != 0x80) {
    goto fail;
  }
  U->UnicodePoint <<= 6;
  U->UnicodePoint |= (ch & 0x3F);
  // If last continuation character ...
  if (--(U->i) == 0) {
    // If codepoint out of range ...
    if ((U->UnicodePoint < U->Min) || (U->UnicodePoint > 0x10FFFF) 
        || IsSurrogate(U->UnicodePoint)) {
      goto fail;
    }
    return false /* No more needed */;
  }
  return true; /* More needed */

  fail:
  U->UnicodePoint = -1;
  U->i = 0;
  U->e = true;
  return false /* No more needed */;
}

/* return 0:OK, else error */
bool ConvertUTF8toUnicodeCodepoints(const char *UTF8, size_t Length, 
    uint32_t *CodePoints, size_t *OutLen) {
  UTF_T U = { 0 };
  *OutLen = 0;
  for (size_t i = 0; i < Length;) {
    while (Put8(&U, UTF8[i++])) {
      // Needed bytes not available?
      if (i >= Length) {
        return true;
      }
    }
    if (U.e) break;
    CodePoints[(*OutLen)++] = U.UnicodePoint;
  }
  return U.e;
}

这是基于一些旧代码,请告知,因为它可能不符合当前标准。
不是最漂亮的goto和神奇的数字。

这种方法的好处不是CodePoints[(*OutLen)++] = U.UnicodePoint消耗代码点,如果想要提取 UTF16(BE 或 LE),可以轻松地为UTF_T块编写消费者代码,而无需更改为 UTF8 -> 代码点部分。

于 2013-11-11T22:53:23.557 回答
1

如果您有一个真实的操作系统和托管的 C 环境供您使用,最好的方法是简单地确保您的程序在使用 UTF-8 作为其编码和使用的语言环境中运行,mbrtowc或者mbtowc将 UTF-8 序列转换为 Unicode 代码点值(wchar_t是 Linux 和任何定义的 C 实现上的 Unicode 代码点编号__STDC_ISO_10646__)。

如果您确实想跳过系统库例程并自己进行 UTF-8 解码,请小心。我曾经使用 Google 代码搜索进行了一项临时调查,发现野外 UTF-8 代码的 1/3 到 2/3 之间的某个地方是危险的错误。这是我强烈推荐的一个完全正确、快速且简单的实现:

http://bjoern.hoehrmann.de/utf-8/decoder/dfa/

我在 musl 中的实现在二进制大小上有点小,而且似乎更快,但也有点难以理解。

于 2013-11-12T04:01:25.280 回答
0

我会使用 GLib 的Unicode 操作函数,这是一个 LGPL 许可的实用程序库。听起来g_utf8_to_ucs4()是您正在寻找的。

于 2013-11-12T14:07:51.470 回答