在开始转换之前,先在 Windows 下用 UE 编辑一个纯文本文件,分别另存为 “UTF-8” 和 “UTF-8 无 BOM” 两种格式。
UTF-8 默认是有 BOM (Byte Order Mark,标识编码标记),在 Windows 里,这个 BOM 为 3 个字节,依次为 ”0xEF,0xBB, 0xBF“ 。顺便说一下,在 Windows 里,Unicode (UTF-16) 的 BOM 为 “0xFF, 0xFE” 。
在 Linux 下编辑出来的文本格式默认为 UTF-8,无 BOM 标记。如果用一些高级的文本编辑器(Windows 7 里的记事本也可以)是可以自动识别这种格式并显示出来,但是当你在命令行里使用 type 命令显示这个文本时显示的就是乱码!如下所示:C:\Users\Administrator\Desktop>type utf8-linux.txt
锘夸綘濂戒笘鐣?
C:\Users\Administrator\Desktop>type 新建文本文档.txt
你好世界 上面,utf8-linux.txt 是从 linux 那里拷贝过来的一个文件,新建文本文档.txt 是在 Windows 下建立的文件。之所以一个显示正常,一个不正常,那是因为 type 命令并不对 UTF-8 的文本转换为 GB2312 的格式。通过对命令行窗口的属性也可以看到简体中文系统的命令行窗口默认支持 GBK 编码显示。
![]()
这就引出一个问题,当我们编程时处理 UTF-8 文本时,也会遭遇乱码的情况。解决的办法是利用微软提供的两个函数 MultiByteToWideChar() 和 WideCharToMultiByte() 。
为了做这个实验,现在还是利用 UE 编辑器编辑一个 UTF-8 (有 BOM) 的文本,里面就只有一句话 ”神马都是浮云“ 。保存后,可以在 UE 里按下 ctrl + h 快捷键看一下这个文本的十六进制码,你会发现前面的 3 个字节就是 ”0xEF,0xBB, 0xBF“ 。
下面编制程序,先读入这个文本,然后进行转换,最后将转换后将结果正常的显示在命令行窗口,另保存一个副本到程序所在的当前文件夹下。
代码如下:
[C++] 纯文本查看 复制代码
// Author: [email]Beyes@groad.net[/email]
#include "stdafx.h"
#define BUF_SIZE 0x200
int _tmain(int argc, LPCTSTR argv[])
{
BYTE mbuf [BUF_SIZE];
BYTE wbuf [BUF_SIZE];
LPBYTE mbufp = mbuf;
HANDLE hInFile, hOutFile, hOutFile2;
DWORD nIn, nOut;
DWORD GB2312;
hInFile = CreateFile(argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
hOutFile2 = CreateFile(argv[2], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
hOutFile = GetStdHandle(STD_OUTPUT_HANDLE);
ReadFile (hInFile, mbuf, BUF_SIZE, &nIn, NULL);
mbuf[nIn] = '\0'; //避免数组越界
GB2312 = ((nIn - 3) / 3) * 2; //处理转换后的文本长度
MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)mbufp, -1, (LPWSTR)wbuf,0x200 ); //先将 UTF-8 转为 Unicode
WideCharToMultiByte (CP_ACP, 0, (LPWSTR)wbuf, -1, (LPSTR)mbuf, 0x200, 0, 0); //再将 Unicode 转为 GB2312
mbufp += 1;
WriteFile (hOutFile, mbufp, GB2312, &nOut, NULL);
WriteFile (hOutFile2, mbufp, GB2312, &nOut, NULL);
CloseHandle(hInFile);
CloseHandle(hOutFile);
CloseHandle(hOutFile2);
return 0;
}
运行输出:D:\WindowsAPP\chinese\Debug>chinese.exe utf8_default.txt else.txt
神马都是浮云 另外,还会生成一个 else.txt 文件。
此时可以再建立一个文本,内容也是 ”神马都是浮云“,然后默认保存。再用 UE 打开,并查看其十六进制形式,你会发现其中的内容和生成的 else.txt 一样。
在程序中声明了两个数组 mbuf 和 wbuf 。mbuf 用来保存读入的 UTF-8 文本内容,wbuf 用来保存将 UTF-8 转换为 Unicode 后的内容,最后将 Unicode 的内容再转换为 GB2312 并保存在 mbuf 中,以供输出和生存文本副本。
这里使用 ReadFile() 函数将文本读入 mbuf 中,读入的数据的字节数保存在 nIn 变量中,为了防止输出数组内容时不小心越界,所以在读入的字符串后面添加一个 '\0' ,而且对于以 null 结尾的字符串,MultiByteToWideChar() 函数中的第 4 个参数便可设置为 -1 。
在 MultiByteToWideChar() 函数里,第 1 个参数 CP_UTF8 表示我们要进行 UTF8 文档的转换;第 2 个对于转换的类型是 UTF8 的话,要设置为 0;第 3 个参数指定缓存空间;第 4 个参数设为 -1,因为我们缓冲区中的字符串是以 null 结尾的;第 5 个参数可选,如果写上,那么就指定用以接收转换结果的缓冲区,所以这里指定 wbuf ;第 5 个参数指定接收转换结果缓冲区的长度。
接着使用 WideCharToMultiByte() 函数对上面获得的 Unicode 编码转换到 GB2312 。第 1 个参数 CP_ACP 表示系统默认的代码页,我用的是简体中文系统,自然这里的代码页就是 GB2312 ;第 2 个参数和上面相同原因设为 0;第 3 个参数指定缓存空间 wbuf,这里由于类型需要,强制转换为 LPWSTR 型,表示 wide char 类型字符指针;第 4 个参数设为 -1;第 5 个参数指定转换后的结果存放的 buf,这里还将结果放回到 mbuf 中;第 6 个参数指定 mbuf 的长度;第 6 和第 7 个参数为可选,一般设置为 0 。
经过上面两个函数的转换,我们已经获得了转换结果并保存到 mbuf 中了。但是,还不能直接的输出 mbuf 里的转换结果。这是因为,在 mbuf 里除了我们想要的正常转换数据外,还有一些冗余数据。比如当用 WideCharToMultiByte() 转换时,之前的 Unicode 中的 BOM 码 "0xFF,0xFE” 会被转为 0x3F,这是单个字节数据,而 GB2312 汉字是双字节数据,所以如果全部显示,那么 0x3F 在输出时一定是个 '?' 号。
此外,因为 UTF-8 是 3 个字节表示一个汉字,而 GB2312 是 2 个字节,所以转换后,为了正确输出,我们要剪掉那些冗余的数据。所以程序中使用 mbufp += 1; 来调整了一下缓冲区的位置,目的去去掉第 1 个冗余字符 0x3F 。GB2312 = ((nIn - 3) / 3) * 2; 中的 (nIn - 3) 表示去掉原 BOM 中的 3 个字节,然后将结果除以 3 表示原来一共有多少个汉字,最后乘以 2 表示这些汉字用 GB2312 来表示时共需要多少个字节。 |