原文发表于:http://www.hellocpp.net/Articles/Article/722.aspx
其实对我来说 ,这篇文章里要说的问题早就碰到过了。今天只是心血来潮,详细归纳一下。
bug描述:在中文日文系统上创建一个中文日文名字的用户名,在这个用户的目录下创建文件失败。
经过检查,发现只要文件名里带中文或者日文等非英文字符,用fstream创建文件就失败。这个问题第一次用vc2005其实我就碰到过了。
当初的解决方法无非是几个:
1. 在程序启动的时候调用setlocale(LC_ALL,"");
2. 不用fstream,改用fopen
3. 在用fstream的时候,用 wchar_t* 做参数。
那么为什么会产生这个问题呢?原因是CRT中的默认locale在vc++2005/2008下是"C",不是系统默认的,而且,新的STL现在都会把你的char*的文件名参数转化成wchar_t*,这个动作将会使用CRT的locale,所以你传递给vc++的文件名中如果带有中文字符并且用C的locale来转换的话,必然会出现乱码。 那么以上的方法就很容易解释了。
1. setlocale(LC_ALL,"")会让CRT使用系统默认的locale,如果你是用Windows XP/Vista/Win7的话,这个应该就是 区域语言选项里的 位置和 非unicode程序位置。那么自然转换就能正确了。
2. fopen即使内部也是通过win32 API的CreateFile来转化,也会使用系统的locale跟CRT的无关。
3. 你自己都转好了当然没有问题了。
总结一下。其实个人感觉方法3才是王道,你所有的东西都用unicode才是最完美的。
当然问题并不到此为止。
在有些locale下,有些很细小的区别,比如:德文系统下,小数是用 0,1来表示的,而不是常见的0.1. 那么如果你的配置文件里写的是0.1。你调用setlocale(LC_ALL,"")后,在德文系统下,你的0.1用sscanf读出来的将会是错误的值:0.0f。是不是感到很晕眩,呵呵,也许问题还不只这些。 一般Locale包含很多选项,比如LC_CTYPE,LC_NUMERIC LC_CURRENCY,等等。LC_NUMERIC就是影响这个数字的表达形式的。
因此,如果你只是想让C++ STL能正常打开非英文字符的文件名,那么你只需要 setlocale(LC_CTYPE,"")就可以了。
目前只总结到这里,有其他发现以后再补充,有不对的地方也欢迎大家提出来。
补充:关于使用setlocale设置后,cout不能输出问题,由于我从来不是用cout输出(我一般采用printf打印到控制台+OutputDebugString输出到调试窗口的log模式)。所以本文没有提到。 如果关心这块,可以在打开文件前设置locale,打开文件结束后设置会默认locale:
locale lc = setlocale(LC_ALL , "");
fstream f(".....");
setlocale(LC_ALL , lc);