需求

有一个 xml 文件,是 utf16-be 格式的,直接使用 firefox 打开不对,所以考虑先转码为 utf8 然后再打开。

解决

int Utf16leToUtf8(uint8_t* dest, int dest_size, int* dest_len, uint16_t* src, int src_len)
{
    uint8_t* dest_start = dest;
    uint8_t* dest_end = dest + dest_size;
    //uint16_t* src_end = (uint16_t*)((uint8_t*)src + src_len);
    uint16_t* src_end = src + src_len;

    while (src < src_end)
    {
      if (*src < UTF16_2 && dest + 1 < dest_end) {
	  //0000 - 007F : 0xxxxxxx
	  *dest++ = (uint8_t)*src;
      } else if(*src >= UTF16_2 && *src < UTF16_3
	  && dest + 2 < dest_end) {
	  //0080 - 07FF : 110xxxxx 10xxxxxx
	  *dest++ = ((*src >> 6) & 0x1F) | 0xC0;
	  *dest++ = (*src & 0x3F) | 0x80;
      } else if(*src >= UTF16_3 && *src < UTF16_4
	  && dest + 3 < dest_end) {
	  //0800 - FFFF : 1110xxxx 10xxxxxx 10xxxxxx
	  *dest++ = ((*src >> 12) & 0x0F) | 0xE0;
	  *dest++ = ((*src >> 6) & 0x3F) | 0x80;
	  *dest++ = (*src & 0x3F) | 0x80;
      } else if(*src >= UTF16_4 && *src < UTF16_4_SECOND
	  && dest + 4 < dest_end) {
	  // 代理项对部分(4字节表示)
	  //0001 0000 - 001F FFFF : 1111 0xxx 10xxxxxx 10xxxxxx 10xxxxxx
	  // 从代理项对到UNICODE代码点转换
	  // 1、从高代理项减去0xD800,获取有效10bit
	  // 2、从低代理项减去0xDC00,获取有效10bit
	  // 3、加上0x10000,获取UNICODE代码点值
	  uint16_t high_sur = *src++;
	  uint16_t low_sur = *src;
	  uint32_t code_point = high_sur - UTF16_4;
	  code_point <<= 10;
	  code_point |= low_sur - UTF16_4_SECOND;
	  code_point += UTF16_4_PLUS;

	  *dest++ = (code_point >> 18) | 0xF0;
	  *dest++ = ((code_point >> 12) & 0x3F) | 0x80;
	  *dest++ = ((code_point >> 06) & 0x3F) | 0x80;
	  *dest++ = (code_point & 0x3F) | 0x80;
      } else {
	  break;
      }

      src++;
    }

    *dest = 0;
    *dest_len = dest - dest_start;

    if (src < src_end) {
      return -1;
    }

    return 0;
}

int Utf16beToUtf8(uint8_t* dest, int dest_size, int* dest_len, uint16_t* src, int src_len)
{
    uint16_t le[src_len];

    union {
      uint16_t s;
      uint8_t buf[sizeof(uint16_t)];
    } data;

    for (int i = 0; i < src_len; i++) {
      /*
      uint8_t * d = (uint8_t *)(src + i);
      uint8_t tmp = *d;
      *d = *(d + 1);
      *(d + 1) = tmp;
      */

      data.s = *(src + i);
      uint8_t tmp = data.buf[0];
      data.buf[0] = data.buf[1];
      data.buf[1] = tmp;
      *(le + i) = data.s;
    }

    return Utf16leToUtf8(dest, dest_size, dest_len, le, src_len);
}

参考

C++ UTF8和UTF16互转代码

UTF8 UTF16 之间的互相转换

UTF-8和BOM的一些说明

UTF-16BE编码的xml文件提取数据并做平滑曲线图