牵着老婆满街逛

严以律己,宽以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

关于std::fstream以及std::ifstream打开中文路径名失败的问题和解决方法

碰到的问题:
      今天碰到这个问题了,是在使用Crypto++库的时候遇到的,该库操作文件是使用的std::ifstream。在我给文件生成签名的时候,每每碰到中文路径名就出错,后来跟进库代码一看是打开文件的时候出错。
据说这个问题在VS2003以及之前版本是没有的,不幸的是我现在用的是VS2005的版本。


产生问题的原因:
    究竟是因为什么产生的这个问题呢?如果你跟进去VC实现版的STL代码,你会发现,它有一个将传入的char字符串文件名转换为UNICODE的wchar_t字符串这样一个过程,其代码如下:
_Fiopen(const char *filename,
    ios_base::openmode mode, 
int prot)
    
{    // open wide-named file with byte name
    wchar_t wc_name[FILENAME_MAX];


    
if (mbstowcs_s(NULL, wc_name, FILENAME_MAX, filename, FILENAME_MAX - 1!= 0)
        
return (0);
    
return _Fiopen(wc_name, mode, prot);
    }
wbstowcs_s方法最终进入到了_mbstowcs_l_helper方法,
如果得到的是C locale,则它认为传进来的字符串为ASCII码,也就是单字节字符,它仅仅是进行了char到wchar_t指针的转换而已,那很显然第二个字节肯定为零,自然的字符就错了;
如果不是的话,它认为是多字节字符,将会调用MultiByteToWideChar进行转码。
在VC8里面,local默认是C locale,所以就出错了。以下为摘抄的该段代码:
    if (_loc_update.GetLocaleT()->locinfo->lc_handle[LC_CTYPE] == _CLOCALEHANDLE)
        
{
            
/* C locale: easy and fast */
            
while (count < n)
            
{
                
*pwcs = (wchar_t) ((unsigned char)s[count]);
                
if (!s[count])
                    
return count;
                count
++;
                pwcs
++;
            }

            
return count;

        }
 else {
            
int bytecnt, charcnt;
            unsigned 
char *p;

            
/* Assume that the buffer is large enough */
            
if ( (count = MultiByteToWideChar( _loc_update.GetLocaleT()->locinfo->lc_codepage,
                                               MB_PRECOMPOSED 
|
                                                MB_ERR_INVALID_CHARS,
                                               s,
                                               
-1,
                                               pwcs,
                                               (
int)n )) != 0 )
                
return count - 1/* don't count NUL */
知道了问题的缘由,才能够更好的解决问题。在VC6和VC7都没有经过这个步骤,好像是直接调用的SDK的CreateFile方法,因此就没有问题。而VC8这样根据locale来转码所以造成了问题。

解决办法:
1、使用C语言的函数设置为中文运行环境

setlocale(LC_ALL, "Chinese-simplified");

其中参数一有以下几个值:

/* Locale categories */

#define LC_ALL          0
#define LC_COLLATE      1
#define LC_CTYPE        2
#define LC_MONETARY     3
#define LC_NUMERIC      4
#define LC_TIME         5

#define LC_MIN          LC_ALL
#define LC_MAX          LC_TIME

这些值的意义可以在MSDN当中查到。
从上面所贴出来的代码可以知道wbstowcs_s方法依赖着locale的LC_CTYPE分类的数值。

2、使用STL函数设置为系统语言环境

std::locale::global(std::locale(""));
建议用这个方法,因为更C++一些,而且可以很容易的恢复之前的locale,以下会说到这个。

最终的解决方法:
经过了我的测试,两种方法都是可用的。
但是,接着又有问题出现了,std::cout输出中文时候,中文是输出不了的!这可真是令人烦扰了。要解决这个问题那么就只能是将代码页再设置回去了,用以下方法可以很好解决问题。
    //设置代码页为简体中文,936是简体中文的代码页。
    std::locale loc1 = std::locale::global(std::locale(".936"));
{
    
// 在这里使用std::ifstream 或者 std::fstream
}
 

    
//恢复原来的代码页
    std::locale::global(std::locale(loc1));
这个恐怕是我现在能够得到的最佳方法了。


参考资料:
C语言locale介绍:
http://www.cnblogs.com/floerggyy/archive/2008/04/15/1154738.html
VC2005(英文版)处理文件名为中文的文件: http://hi.baidu.com/ark803/blog/item/9d54661f4ff25866f724e470.html

posted on 2008-09-15 21:04 杨粼波 阅读(3863) 评论(3)  编辑 收藏 引用

评论

# re: 关于std::fstream以及std::ifstream打开中文路径名失败的问题和解决方法 2008-11-03 21:34 QQ87567844

可是我用的VC2005中文版为什么也不能读取和生成中文文件名的文件呢。  回复  更多评论   

# re: 关于std::fstream以及std::ifstream打开中文路径名失败的问题和解决方法[未登录] 2008-11-04 23:05 杨粼波

汉化的只不过是VS的UI而已,和编译器没有必然的关系。  回复  更多评论   

# re: 关于std::fstream以及std::ifstream打开中文路径名失败的问题和解决方法 2008-11-04 23:34 陈梓瀚(vczh)

setlocale("chs");  回复  更多评论   


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理