跨平台 UTF-8 字符文件数据 encoding/decoding

Cross platform UTF-8 char file data encoding/decoding

我使用数据结构对二进制文件进行编码,其中 属性 之一是 wchar_t 类型以支持 UTF-8。

每个结构如下所示:

 struct DataBlock{

    wchar_t charcode;
    int width;
    int height;
   ///etc

  }

编码发生在 Windows,其中 wchar_t 大小为 2 个字节。

文件解码发生在Linux,大小为 4 bytes.So Linux 侧的 charcode 读出值错误。

在不对 UTF 使用 3rd party libs 的情况下解决该差异的最佳方法是什么?是否可以编码字符码,例如在 win 上编码为 'int' 数据类型,然后在 [=29 上编码为 'int' 数据类型=] 投射到 wchar_t?

unicode 完整字符集当前需要 32 位来表示所有可能的值:

  • UTF-32 编码将这些字符存储在 4 个字节中,也就是一个 uint32_t
  • UTF-16编码将每个unicode字符存储成一两个uint16_t
  • UTF-8编码将每个unicode字符存储为一到四个uint8_t

通常,windows uses wchar_t to store unicode text in UTF-16 encoding. At the time this was decided, UTF-16 was able to hold all the unicode caracter set, which is no longer true today。 Linux 使用 UTF-8 编码。大多数实现使用 char 以 UTF-8 格式存储 unicode 文本。

该标准为您提供了一些工具来应对编码转换

  • 当reading/writing流时,您可以使用wbuffer_convert together with codecvt面在wchar_tUTF16和UTF8编码之间转换。

  • 您还可以使用wstring_convert在UTF16和UTF8之间转换已经加载到内存中的字符串。

如果你只想在二进制文件中使用跨系统数据结构而不进行转换,只需使用:

struct DataBlock{
    uint16_t charcode;   // if you assing in windows from a wchar_t, no problem
    ...
}

编写二进制结构本质上是不可移植的。坏事几乎无处不在:

  • 对于任何大于 char 的类型,您都可能遇到字节顺序问题
  • 对于任何小于 8 字节的类型,您都可能会遇到对齐问题 - 即使可以在支持它的体系结构和编译器上使用 #pragma 来缓解这种问题。

您应该避免这种情况,而是使用一种 marshalling,即以明确且独立于体系结构的方式进行序列化。例如:

  • wchar_t charcode - 假设你的 charcode 永远不会使用超过 2 个字节,你明确地将它转换为 char[2] (实际上我强制使用 2 个字节大字节序表示):

    code[0] = (charcode >> 8) & 0xFF;
    code[1] = charcode & 0xFF;
    
  • int - 知道你是否需要2、4或8个字节来表示width和[=的任何值21=];假设它是 4 (int32_tuint32_t)

    code[0] = (width >> 24) & 0xFF;
    code[1] = (width >> 16) & 0xFF;
    code[2] = (width >> 8) & 0xFF;
    code[3] = width & 0xFF;
    

因此,您在具有确定大小的 char 数组中明确定义 struct DataBlock 的转换。现在您确实拥有了可移植到任何网络、体系结构或编译器上的东西。当然,您已经明确地编写了用于编码和解码的 2 例程,但这是我所知道的拥有 可移植二进制结构 .

的唯一方法

希望htonx功能可以帮到您。它们明确采用 16 位或 32 位整数,并强制按 network(大端)顺序进行转换。来自 Linux 手册页:

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

htonl() 函数将无符号整数 hostlong 从主机字节顺序转换为网络字节顺序。

htons()函数将无符号短整数hostshort从主机字节序转换为网络字节序。

ntohl() 函数将无符号整数 netlong 从网络字节顺序转换为主机字节顺序。

ntohs() 函数将无符号短整数 netshort 从网络字节顺序转换为主机字节顺序。

这样,您就可以直接编写结构的字段:

long l = htonl(data.charcode); // or htons if you only need 16 bits
fwrite(&l, sizeof(long), 1, fdout); // sizeof(short) if you used 16 bits

阅读也一样:

long l;
fread(&l, sizeof(long), 1, fdin);
data.charcode = ntohl(l);

这个函数已经在类 Unix 系统下定义了很长时间,并且似乎在最近版本的 Windows 编译器上定义。

当然,如果你绝对确定你只会使用小端架构,你甚至可以不转换为字节序。但请务必在您的文档中更正,最好使用红色闪烁字体...