char 类型并将 ASCII 文本重新编码为 UTF-16

char type and re-encoding ASCII text into UTF-16

我正在使用 libiconv 将我的字符数组转换为 UTF-16 字符串。我有疑问。

  1. iconv 函数的签名

    size_t iconv(iconv_t cd,
                 const char* * inbuf, size_t *inbytesleft,
                 char* * outbuf, size_t *outbytesleft);
    

    这意味着,char 用于保存要转换为的任何类型的字符(char 与 wide char)。 我在学校的 C 老师教我,对于奇怪或不可读的字符,我们应该使用 wchar_t。我现在很困惑。

  2. 我在 input = "KOTEX" 上测试了这个方法作为 ASCII 编码类型,并希望输出另一个双倍长度的字符串编码为 UTF-16。它立即失败。但是,如果我将目标代码页更改为 UTF-8,它会起作用,但返回的数据会丢失。这是为什么?

iconv 的缓冲区参数实际上是 char * 但这并不意味着它们实际上表示 C 字符串。 (如果界面改用 uint8_t* 可能不会那么令人困惑,但这是不合时宜的;iconvstdint.h 之前就存在了)

Posix 标准(和 Linux 联机帮助页)试图阐明这一点:

The type of inbuf and outbuf, char **, does not imply that the objects pointed to are interpreted as null-terminated C strings or arrays of characters. Any interpretation of a byte sequence that represents a character in a given character set encoding scheme is done internally within the codeset converters. (Posix.2008

因此,如果您计划转换为 UTF-16,则应提供一个具有适用于 UTF-16 的数据类型的输出缓冲区。 wchar_t 不是 合适的数据类型;在许多系统上,它会太大。 uint16_t 就好了。

请注意,实际上存在三种不同的 UTF-16 编码(名称取决于系统;此处的名称可被 Gnu iconv 识别):

  • UTF16LE(或UTF-16LE):"Little endian" UTF-16。在这种格式中,每个字符的低位字节在前,然后是高位字节。 KOTEX

    {0x4B, 0x00, 0x4F, 0x00, 0x54, 0x00, 0x45, 0x00, 0x58, 0x00}
    
  • UTF16BE(或UTF-16BE):"Big endian" UTF-16。在这种格式中,每个字符的高位字节在前,然后是低位字节。 KOTEX 是:

    {0x00, 0x4B, 0x00, 0x4F, 0x00, 0x54, 0x00, 0x45, 0x00, 0x58}
    
  • UTF16(或UTF-16):UTF16BEUTF16LE,取决于机器是大端还是小端;转换后的字符串以 Byte Order Mark (BOM) 开头。在小端机器(我的)上,KOTEX

    {0xFF, 0xFE, 0x4B, 0x00, 0x4F, 0x00, 0x54, 0x00, 0x45, 0x00, 0x58, 0x00}
    

    在大端机器上,它将是:

    {0xFE, 0xFF, 0x00, 0x4B, 0x00, 0x4F, 0x00, 0x54, 0x00, 0x45, 0x00, 0x58}
    

UTF16(未经修饰的字节序规范)总是以 BOM 开头的事实意味着您必须记住在输出缓冲区中提供一个额外的(2 字节)字符。否则,您将得到 E2BIG.

在所有这三种编码中,basic multilingual plane (BMP) require two (two-byte) character positions, a so-called surrogate pair 之外的字符。所有 ascii 字符都在 BMP 上,因此您无需担心 ascii 到 utf16 的转换,但如果您正在执行 utf8 到 utf16,则您会担心。