click to login
某不知名程序员的网上巢穴。稍安勿躁,正在开发。

浅谈操作系统宽字符编码的兼容问题

稍早之前有次在写 Linux 平台的程序的时候,需要实现在 Linux 平台读取在 Windows 中以宽字符 wchar_t 格式存储的数据文件,结果发现数据不能以 wchar_t 格式的字符串形式正常读取。将 Buffer 打印出来看,发现输出的内容甚是奇怪:所有的字符都是隔字输出,并且在有的位置还是有很多乱码。

后来查阅相关资料发现,在 Windows 中 wchar_t 格式的每个字符在内存中占 2 字节,而 Linux 中 wchar_t 占 4 字节。这就导致了前面所说的隔字输出和乱码的问题。Linux 并不能识别 Windows 平台生成的 wchar_t 格式的数据,那么需要在 Linux 中读取使用这些数据,就需要首先进行一些转换。

首先想到的是使用 wcstombs / mbstowcs 等函数进行转换操作。

#include <locale.h>
setlocale(LC_ALL, "");

wcstombs(char * to,wchar_t * from,size_t _maxCount);
mbstowcs(wchar_t * to,char * from,size_t _maxCount);

发现仍旧不能成功实现,才想起由于 Linux 并不能识别 Windows 平台生成的 wchar_t 格式的数据,那么当然不能使用 wcstombs 将其先转换成 MBS 格式的数据、再由 MBS 转换成 WCS 格式的数据了。后来通过查阅相关资料发现 libiconv 库能够实现多种编码格式的相互转换。

MBS:Multi Byte String,用 char 作为存储类型,一个字符可能对应 1 个或者多个 char,不能直接确定字符边界。charset 不确定。

WCS:Wide Character String,用 wchar_t 作为存储类型,一个字符对于一个 wchar_t。使用 unicode 编码,charset 与 OS 相关,在 Windows 平台中为 UTF16(UCS-2),在大多数 unix 平台中为 UTF32(UCS-4)。

UCS:Unicode Character Set,UCS-2 规定了 2 个字节代表一个文字,UCS-4 规定了 4 个字节代表一个文字。

libiconv 是一个开源的字符编码转换库,可以方便地完成几乎所有的编码转换工作。它常用的接口就三个,iconv_open / iconv / iconv_close。

以下是转换的代码:

size_t result;
iconv_t env;
env = iconv_open("UCS-4LE","UCS-2LE");
if (env==(iconv_t)-1)
{
    printf("iconv_open error %d\n",errno);
    return -1;
}
result = iconv(env, (char**)&szSrc, (size_t*)&nSize, (char**)&szDest, (size_t*)&nDestSize);
if (result==(size_t)-1)
{
    printf("UTF16 -> UTF32 conv error %d\n",errno) ;
    return -1;
}
iconv_close(env);

这样就实现了 Windows 的 wchar_t 向 Linux 支持的 wchar_t 格式的数据的转换了。

其中 iconv_open 函数的参数为源字符集格式和目标字符集格式,char* 字符串描述符。在编写代码时指定该函数的参数之前,应通过 iconv --list 指令查看当前编译环境所在的操作系统支持的字符集列表中,是否包含自己指定的该字符集。我最近刚在这个位置犯了错误,同一套代码在不同的 Linux 环境下编译,一个成功一个失败。

当然解决问题之后,即采用较为通用的字符集描述符就可以了,尽量别使用与机器相关的 *-INTERNAL 系列字符集描述符。

在上面的代码中使用的两个参数:UCS_4LE 和 UCS-2LE,均采用小端对齐的对齐方式。

1) Little-Endian 就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。

2) Big-Endian 就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

16bit 宽的数 0x1234 在 Little-endian 模式(以及 Big-endian 模式)CPU 内存中的存放方式(假设从地址 0x4000 开始存放)为:

内存地址	小端模式存放内容	大端模式存放内容
0x4000  0x34            0x12
0x4001  0x12            0x34

Libiconv 库的相关资料:http://www.gnu.org/software/libiconv/

没有回答

评论:

版权所有 © 2010-2016 小刀志 · 本站基于 WordPress 构建 · 原创内容转载请取得作者同意和授权