C++文本文件无法保存为Unicode格式,仍然以ANSI格式保存。

5
基本上,我需要能够创建一个Unicode文本文件,但无论我做什么它都会保存为ANSI格式。
以下是我的代码:
    wchar_t name[] = L"‎中國哲學書電子化計劃";
    FILE * pFile;
    pFile = fopen("chineseLetters.txt", "w");

    fwrite(name, sizeof(wchar_t), sizeof(name), pFile);
    fclose(pFile);

这是我的“chineseLetters.txt”的输出结果:

     -NWòTx[øfû–P[SŠƒR  õ2123

此外,该应用程序采用MBCS格式,无法改为Unicode,因为它需要与Unicode和ANSI都能正常工作。

我非常希望能得到一些帮助。谢谢。

感谢所有快速回复的人!它有效了!

仅仅添加L"\uFFFE‎中國哲學書電子化計劃"仍然不起作用,文本编辑器仍将其识别为CP1252,所以我进行了两次fwrite,一次用于BOM,一次用于字符。以下是我的代码:

    wchar_t name[] = L"‎中國哲學書電子化計劃";
    unsigned char bom[] = { 0xFF, 0xFE };
    FILE * pFile;
    pFile = fopen("chineseLetters.txt", "w");
    fwrite(bom, sizeof(unsigned char), sizeof(bom), pFile);
    fwrite(name, sizeof(wchar_t), wcslen(name), pFile);
    fclose(pFile);

5
我不想只是给你一个链接,但首先,你必须阅读这篇文章:关于Unicode和字符集,每个软件开发者绝对必须知道的最低限度(没有任何借口!) - Konrad Rudolph
1
对我来说,它没有解决你的问题,但我认为你的 frwite() 调用应该是:fwrite(name, sizeof(wchar_t), sizeof(name)/sizeof(wchar_t), pFile); - Galik
1
我怀疑你的问题可能是将UTF-8字符(通过文本编辑器)放入固定大小(wchar_t)的字符字符串中。如果你使用char而不是wchar_t(假设我的修复已经完成),那么对我来说它可以正常工作。 - Galik
只需要在文件开头写入两个字节:FF FE。然后写入你的文本,看看会发生什么。 - Mustafa Chelik
1
一直会不断重复的话:使用UTF-8,你所有的问题都会消失。请参考http://utf8everywhere.org。 - Pavel Radzivilovsky
显示剩余4条评论
2个回答

4
我需要能够创建Unicode文本文件。 Unicode不是一种编码方式,您是否指UTF-16LE?这是Windows x86 / x64在内存中使用的双字节编码单元编码,以及一些Windows应用程序(如记事本)在其UI中误导性地将UTF-16LE描述为“Unicode”。 您已直接将字符串的内存存储复制到文件中。如果您在Windows / MSVCRT下编译此代码,则由于内部存储编码为UTF-16LE,因此生成的文件将被编码为UTF-16LE。如果您在其他环境中编译此代码,将获得不同的结果。 如果您将文件错误地解释为Windows代码页1252(西欧),则UTF-16LE编码数据的输出将类似于“chineseLetters.txt”的输出。
如果您已将文件加载到Windows应用程序(例如记事本)中,则可能不知道该文件包含UTF-16LE编码数据,因此默认使用您的默认区域设置特定(ANSI,mbcs)代码页作为编码来读取文件,导致上述乱码
当您制作UTF-16文件时,应在其开头放置一个字节顺序标记字符U+FEFF,以让使用者知道它是UTF-16LE还是UTF-16BE。这也会给像记事本这样的应用程序一个提示,告诉它该文件包含UTF-16,而不是ANSI。因此,您可能会发现编写L"\uFEFF‎中國哲學書電子化計劃"会使输出文件在记事本中显示得更好。

但最好将转换为以明确方式声明的特定所需编码(例如UTF-8)的< char >字节,而不是依赖于C库在内存中使用的存储格式。在Win32上,您可以使用{{link1:WideCharToMultibyte API}}或像Mr.C64所描述的宽松的ccs来实现这一点。如果您选择使用ccs编写UTF-16LE文件,则会自动为您放置BOM。


1
请注意,我认为他在使用sizeof()获取整个字符串缓冲区长度时也存在问题:fwrite(name, sizeof(wchar_t), sizeof(name), pFile);。我认为第三个参数应该是**_countof(name)-1**,即wcslen(name) - Mr.C64
不确定您的意思。他正在使用wchar_t [21],而不是依赖于C库所使用的内存存储格式。 - Mooing Duck
在所有平台上,wchar_t 并不相同。在一些平台上,它是16位的(对于UTF-16很有用),但在其他平台上,它是32位的(对于UTF-32很有用)。 wchar_t 不是可移植的数据类型。 C++11中添加了 char16_tchar32_t 来替换它。 - Remy Lebeau
我认为这里的内部存储编码不是UTF-16LE,因为作者写道:“此外,该应用程序采用MBCS编码,无法更改为Unicode,因为它需要与Unicode和ANSI一起使用。”但我不确定MBCS和UTF16之间有什么不同。 - Dexter

3

"Unicode" 是一个通用术语,您可能需要澄清您计划在文件中使用哪种 编码方式

Unicode UTF-8 是常见的选择(它特别适用于在不同平台之间交换文本数据,因为它没有 "字节序" 的概念,与 UTF-16 不同,没有小端和大端的混淆,并且在互联网上被广泛使用),但也有其他选项(例如在 Windows 上的 UTF-16,可以直接映射到 Visual C++ 中的 wchar_t-字符串)。

如果您正在使用 Visual C++,您可以在 fopen() (或 _wfopen())的第二个参数中指定 ccs 属性,选择您想要的编码方式,例如 "ccs=UTF-8" 以使用 UTF-8 编码。
你可以在fopen() MSDN 文档中了解更多细节,例如:

fopen supports Unicode file streams. To open a Unicode file, pass a ccs flag that specifies the desired encoding to fopen, as follows.

fp = fopen("newfile.txt", "rt+, ccs= encoding ");

Allowed values of encoding are UNICODE, UTF-8, and UTF-16LE.

我认为UNICODE指的是UTF-16BE(即大端UTF-16);另外两个选项很清楚。


编辑

我尝试了这段代码,并且在使用Unicode UTF-8保存中文文本方面效果很好(我使用的是Visual Studio 2013):

wchar_t name[] = L"‎中國哲學書電子化計劃";
FILE * file = fopen("C:\\TEMP\\ChineseLetters.txt", "wt, ccs=UTF-8");
...check for error...

fwrite(name, sizeof(wchar_t), _countof(name)-1, file);
fclose(file);

请注意,在将中文文本粘贴到源文件并保存后,Visual Studio编辑器会自动判断需要将源文件保存为Unicode格式以避免丢失文本信息,并显示一个对话框请求确认。
因此,如果您的源文件中有一些“硬编码”的Unicode文本(在生产质量的Windows/C++代码中,您可能希望将文本保存在资源文件中),请考虑将源文件保存为Unicode格式

还要注意,我在fwrite()调用中使用了_countof()而不是sizeof()。你原来是这样写的:

fwrite(name, sizeof(wchar_t), sizeof(name), file);

但是那是错误的,因为你想要在第三个参数中指定wchar_t的数量,而不是总字节数(请注意,在MSVC中,sizeof(wchar_t) == 2,即一个wchar_t等于两个char,即两个字节)。

此外,您需要考虑将-1添加到wchar_t的总缓冲区长度,因为您不希望在Unicode字符串缓冲区中写入NUL-终止的wchar_t
(如果是未知静态大小的Unicode UTF-16 wchar_t字符串,您可以直接使用wcslen()获取不包括终止的NULwchar_t的数量)。

这是正确打开上面的UTF-8文件的方法:

来自UTF-8文件的中文文本在MS Word中的显示


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接