为什么fstream不支持文件名中的em-dash?

时间:2016-11-22 08:17:55

标签: c++ visual-studio-2005

我将一些代码从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文件名,而不仅仅是某些字符?

3 个答案:

答案 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 wfstreamfstream以外的路径不执行任何操作。没有。它只是意味着管理像wchar_t"这样的字节流。 (并且它以人们可以预期的不同方式做到这一点,因此在大多数情况下使这个类无用,但这是另一个历史)。要在Visual Studio实现中使用Unicode接口,它存在重载的构造函数和接受open()的{​​{1}}函数。 const wchar_t*fstream重载了这些函数和构造函数。使用wfstreamfstream
  • 一起使用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"。 (MultiByteToWideCharCP_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")不匹配