我将一些代码从C语言移植到C ++,并且发现包含em-dash的路径存在问题,例如: “C:\ TEMP \测试1.dgn”。即使路径在Visual Studio 2005调试器中正确显示,对fstream :: open()的调用也会失败。
奇怪的是,使用C库fopen()函数的旧代码工作正常。我以为我会尝试使用wfstream类,然后发现使用mbstowcs()转换我的C字符串会完全失去em-dash,这意味着它也会失败。
我认为这是一个区域设置问题,但为什么默认区域设置不支持em-dash?为什么fstream不能处理em-dash?我原以为文件流类支持Windows文件系统支持的任何字节字符。
鉴于这些限制,处理打开文件流的正确方法是什么,该文件流可能包含有效的Windows文件名,而不仅仅是某些字符?
答案 0 :(得分:1)
字符em-dash在UTF-16中编码为U+2014
(小端为0x14 0x20
),UTF-8为0xE2 0x80 0x94
,其他代码根本不符合代码在使用的charset和代码页上。 Windows-1252代码页(在西欧语言中非常常见)具有我们可以认为等效的短划线字符0x97
。
Windows内部管理UTF-16路径,因此每次使用其错误调用的ANSI接口(以A
结尾的函数)调用函数时,路径将使用为用户配置的当前代码页转换为UTF -16。
另一方面,可以实现C和C ++的RTL访问" ANSI"或" Unicode" (以W
)接口结尾的函数。在第一种情况下,用于表示字符串的代码页必须与用于系统的代码页相同。在第二种情况下,我们要么从头开始直接使用utf-16字符串,要么必须将用于转换为utf-16的函数配置为使用源字符串的相同代码页进行映射。
是的,这是一个复杂的问题。并且有几个错误的(或有问题的)建议来解决它:
wfstream
代替fstream
: wfstream
对fstream
以外的路径不执行任何操作。没有。它只是意味着管理像wchar_t
"这样的字节流。 (并且它以人们可以预期的不同方式做到这一点,因此在大多数情况下使这个类无用,但这是另一个历史)。要在Visual Studio实现中使用Unicode接口,它存在重载的构造函数和接受open()
的{{1}}函数。 const wchar_t*
和fstream
重载了这些函数和构造函数。使用wfstream
与fstream
。open()
mbstowcs()
:此处的问题是要使用的区域设置(包含字符串中使用的代码页)。如果您匹配区域设置,因为默认区域设置与系统区域设置匹配,请冷静。如果没有,您可以尝试使用mbstowcs_l()
。但是这些函数是不安全的C函数,因此您必须小心缓冲区大小。无论如何,只有在运行时获得转换路径时,这种方法才有意义。如果它是编译时已知的静态字符串,最好直接在代码中使用它。L"C:\\temp\\test—1.dgn"
:字符串中的L
前缀并不代表"将此字符串转换为utf-16" (源代码用于8位字符),至少在Visual Studio实现中没有。 L
前缀表示"在引号"之间的每个字符后添加0x00
个字节。所以—
,相当于窄(普通)字符串中的字节0x97
,当它在宽(带有0x97 0x00
)字符串的前缀时变为L
,但不是{{1} }}。相反,最好使用其通用字符名称:0x14 0x20
一种流行的方法是在代码中始终使用utf-8或utf-16,并且仅在严格必要时才进行转换。将具有特定代码页的字符串转换为utf-8或utf-16时,尝试首先转换为其中一个(utf-8或utf-16),首先识别正确的代码页。要进行转换,请根据它们的来源使用这些功能。如果从XML文件中获取字符串,那么通常在那里使用所使用的代码页(并使用utf-8)。如果它来自Windows控件,请使用Windows API函数,如L"C:\\temp\\test\\u20141.dgn"
。 (MultiByteToWideChar
或CP_ACP
用作默认代码页。)
始终使用GetACP()
(不是fstream
)及其宽接口(wfstream
和构造函数),而不是它的狭窄接口。 (您可以再次使用open
将utf-8转换为utf-16)。
有几篇文章和帖子提供了这种方法的建议。其中一个我推荐你:http://www.nubaria.com/en/blog/?p=289。
答案 1 :(得分:0)
如果所有你使用宽字符函数的宽字符表达式,这应该可行。也就是说,使用wfstream,但不使用mbstowcs,而是使用前缀为L
char的宽字符串文字:
const wchar_t* filename = L"C:\temp\test—1.dgn";
此外,请确保您的源文件在Visual Studio中保存为UTF-8。否则,em-dash可能会使用em-dash获得区域设置问题。
答案 2 :(得分:0)
为遇到此问题的其他人发布此解决方案。问题是Windows分配了" C"默认情况下启动时的语言环境,并且" Windows-1252"中定义了em-dash(0x97)。代码页,但在" C"使用的普通ASCII表中未映射。语言环境。所以简单的解决方案是打电话:
setlocale ( LC_ALL, "" );
在fstream :: open之前。这会将当前代码页设置为OS定义的代码页。在我的程序中,我想用fstream打开的文件是由用户定义的,因此它位于系统定义的代码页(Windows-1252)中。
因此,虽然摆弄unicode和宽字符可能是避免未映射字符的解决方案,但它并不是问题的根源。实际问题是输入字符串的代码页(" Windows-1252")与Windows程序中默认使用的活动代码页(" C")不匹配