我的问题似乎让人感到困惑。下面是一些具体的东西:
我们的代码如下:
FILE * fout = _tfsopen(_T("丸穴種類.txt"), _T("w"), _SH_DENYNO);
_fputts(W2T(L"刃物種類\n"), fout);
fclose(fout);在MBCS构建目标下,上面为代码页932生成一个正确编码的文件(假设932是运行时的系统默认代码页)。
在UNICODE构建目标下,上面生成的垃圾文件中满是?
我想要定义一个符号,或者使用编译器开关,或者包含一个特殊的标题,或者链接到给定的库,以便在构建目标是UNICODE而不改变源代码的情况下继续工作。
这里有一个问题,因为它曾经存在过:
FILE*流可以以t(ranslated)或b( modes )模式打开。桌面应用程序可以编译为UNICODE或MBCS (在Windows下)。 如果我的应用程序是为MBCS编译的,那么将MBCS字符串写入"wt“流将生成一个格式良好的文本文件,其中包含系统代码页的MBCS文本(即代码页”用于非Unicode软件“)。 由于我们的软件通常使用大多数字符串和流函数的_t版本,所以在MBCS中,生成输出主要由puts(pszMBString)或类似的putc等处理。由于pszMBString已经在系统代码页中(例如,在日本机器上运行时为932 ),字符串将逐字写出(尽管puts和gets会自动按行终止符)。 但是,如果我的应用程序是为UNICODE编译的,那么将MBCS字符串写入"wt“流会导致垃圾(大量的”?“)。(例如,我将UNICODE转换为系统的默认代码页,然后使用fwrite(pszNarrow, 1, length, stream)将其写入流中)。
我可以在二进制模式下打开我的流,在这种情况下我会得到正确的MBCS文本.但是,行终端不再是PC风格的CR+LF,而是UNIX风格的LF。这是因为在二进制(非翻译)模式下,文件流不处理LF->CR+LF转换。
,但我真正需要的是,能够生成与以前编译MBCS完全相同的文件:使用系统的代码页纠正行终止符和MBCS文本文件。
显然,我可以自己手动调整行终止符,并使用二进制流。然而,这是一种非常具有侵入性的方法,因为我现在必须在整个系统中找到编写文本文件的所有代码,并对其进行修改,使其能够正确地完成所有这些工作。令我震惊的是,UNICODE目标比我们以前使用的MBCS目标更愚蠢/能力更低!当然,有一种方法可以切换C库来表示“输出窄字符串就像--但是正确地处理行终止符,就像在MBCS构建中所做的那样”?!
发布于 2013-08-26 17:04:31
可悲的是,这是一个巨大的主题,值得一本专门讨论它的小书。这本书基本上需要为每个目标平台(Linux、Windows风格、Mac等)建立一个专门的章节。
我的回答将只涉及Windows桌面应用程序,它是为C++编译的,不管有没有MFC。请注意:这与希望使用系统默认代码页(即非UNICODE软件的代码页)从Unicode构建中读取和写出MBCS (窄)文件有关。如果要从UNICODE构建中读取和写入Unicode文件,则必须以二进制模式打开这些文件,并且必须手动处理BOM和行提要转换(即在输入时,必须跳过BOM (如果有的话),并且两者都必须将外部编码转换为Windows,即UTF-16 be,以及将任何CR+LF序列转换为LF;输出时,必须编写BOM (如果有的话),并将其从UTF-16 be转换为您想要的任何目标编码,另外还必须将LF转换为CR+LF序列,使其成为格式正确的PC文本文件)。
小心MS的std C库的放置、获取和写等等,如果以文本/翻译模式打开,它将在写入时将任何0x0D转换为0x0A 0x0D序列,在读取时将逆序转换,而不管您是在读取或写入单个字节,还是在编写一个宽字符,还是一个随机二进制数据流--它不在乎,所有这些功能都归结为进行盲字节--在文本/翻译模式下进行转换!
还要注意,许多Windows函数在内部使用CP_ACP,没有对它们的行为进行任何外部控制(例如,WritePrivateProfileString())。因此,您可能希望确保所有库都使用相同的字符区域设置: CP_ACP而不是其他库,因为您无法控制某些函数行为,因此您不得不遵从它们的选择或根本不使用它们。
如果使用MFC,则需要:
// force CP_ACP *not* CP_THREAD_ACP for MFC CString auto-conveters!!!
// this makes MFC's CString and CStdioFile and other interfaces use the
// system default code page, instead of the thread default code page (which is normally "c")
#define _CONVERSION_DONT_USE_THREAD_LOCALE 对于C++和C库,必须告诉库使用系统代码页:
// force C++ and C libraries based on setlocale() to use system locale for narrow strings
// (this automatically calls setlocale() which makes the C library do the same thing as C++ std lib)
// we only change the LC_CTYPE, not collation or date/time formatting
std::locale::global(std::locale(str(boost::format(".%||") % GetACP()).c_str(), LC_CTYPE));在包含任何其他标头之前,我在所有预编译头中执行#define。我将全局区域设置为main (或其道德等效),为整个程序设置一次(您可能需要对要执行I/O或字符串转换的每个线程调用此区域设置)。
构建目标是UNICODE,对于大多数I/O,我们在通过CStringA(my_wide_string)输出之前使用显式字符串转换。
另一件应该注意的事情是,VS C++下的C标准库中有两组不同的多字节函数--它们使用线程的区域设置来进行操作,另一组使用的是_setmbcp() (您可以通过_getmbcp()进行查询)。这是用于所有狭义字符串解释的实际代码页(而不是区域设置)(注意:这总是由VS CP_ACP启动代码初始化为GetACP() )。
有用的参考资料:
发布于 2013-08-21 21:41:49
C库支持窄字符串(char)和宽字符串(wchar_t)。在Windows中,这两种类型的字符串分别称为MBCS (或ANSI)和Unicode。
即使您已经定义了_UNICODE,也完全可以使用狭义函数。无论是否定义了_UNICODE,下列代码都应该产生相同的输出:
FILE* f = fopen("foo.txt", "wt");
fputs("foo\nbar\n", f);
fclose(f);在您的问题中,您写道:“我将UNICODE转换为系统的默认代码页,并将其写入流”。这使我相信,您的宽字符串包含无法转换为当前代码页的字符,因此可以用问号替换每个字符。
也许您可以使用当前代码页以外的其他编码。我建议尽可能使用UTF-8编码。
Update:在运行在代码页1252上的Windows机器上测试示例代码,对_fputts的调用返回-1,表示错误。errno被设置为EILSEQ,意思是“非法字节序列”。MSDN文档 for fopen声明:
当Unicode流-I/O函数以文本模式(默认)操作时,源流或目标流被假定为多字节字符序列。因此,Unicode流输入函数将多字节字符转换为宽字符(就像调用
mbtowc函数一样)。出于同样的原因,Unicode流输出函数将宽字符转换为多字节字符(就像调用wctomb函数一样)。
这是此错误的关键信息。wctomb将使用C标准库的区域设置。通过显式地将C标准库的区域设置为代码页932 (Shift JIS),代码运行良好,输出在输出文件中的Shift JIS中被正确编码。
int main()
{
setlocale(LC_ALL, ".932");
FILE * fout = _wfsopen(L"丸穴種類.txt", L"w", _SH_DENYNO);
fputws(L"刃物種類\n", fout);
fclose(fout);
}另一种(也许更好的)解决方案是在调用C标准库的窄字符串函数之前自己处理转换。
发布于 2013-08-21 14:52:08
在为UNICODE编译时,c++库对MBCS一无所知。如果您说打开文件用于输出文本,它将尝试将传递给它的缓冲区视为UNICODE缓冲区。
另外,MBCS是可变长度编码.要解析它,c++库需要迭代字符,这当然是不可能的,因为它对MBCS一无所知。因此,“只要正确处理行终止符就不可能了”。
我建议您要么预先准备字符串,要么创建自己的函数,将字符串写入文件。不确定一个一个地写字符是否有效(需要测量),但如果不是,您可以分段处理字符串,将不包含\n的所有内容一举完成。
https://stackoverflow.com/questions/18359750
复制相似问题