开发者社区> 杨粼波> 正文

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

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


产生问题的原因:
    究竟是因为什么产生的这个问题呢?如果你跟进去VC实现版的STL代码,你会发现,它有一个将传入的char字符串文件名转换为UNICODE的wchar_t字符串这样一个过程,其代码如下:
None.gif_Fiopen(const char *filename,
None.gif    ios_base::openmode mode, int prot)
ExpandedBlockStart.gif    {    // open wide-named file with byte name
InBlock.gif
    wchar_t wc_name[FILENAME_MAX];
InBlock.gif
InBlock.gif
InBlock.gif    if (mbstowcs_s(NULL, wc_name, FILENAME_MAX, filename, FILENAME_MAX - 1) != 0)
InBlock.gif        return (0);
InBlock.gif    return _Fiopen(wc_name, mode, prot);
ExpandedBlockEnd.gif    }
wbstowcs_s方法最终进入到了_mbstowcs_l_helper方法,
如果得到的是C locale,则它认为传进来的字符串为ASCII码,也就是单字节字符,它仅仅是进行了char到wchar_t指针的转换而已,那很显然第二个字节肯定为零,自然的字符就错了;
如果不是的话,它认为是多字节字符,将会调用MultiByteToWideChar进行转码。
在VC8里面,local默认是C locale,所以就出错了。以下为摘抄的该段代码:
None.gif    if (_loc_update.GetLocaleT()->locinfo->lc_handle[LC_CTYPE] == _CLOCALEHANDLE)
ExpandedBlockStart.gif        {
ExpandedSubBlockStart.gif            /* C locale: easy and fast */
InBlock.gif            while (count < n)
ExpandedSubBlockStart.gif            {
InBlock.gif                *pwcs = (wchar_t) ((unsigned char)s[count]);
InBlock.gif                if (!s[count])
InBlock.gif                    return count;
InBlock.gif                count++;
InBlock.gif                pwcs++;
ExpandedSubBlockEnd.gif            }

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

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

None.gifsetlocale(LC_ALL, "Chinese-simplified");

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

ExpandedBlockStart.gif/* Locale categories */
None.gif
None.gif#define LC_ALL          0
None.gif#define LC_COLLATE      1
None.gif#define LC_CTYPE        2
None.gif#define LC_MONETARY     3
None.gif#define LC_NUMERIC      4
None.gif#define LC_TIME         5
None.gif
None.gif#define LC_MIN          LC_ALL
None.gif#define LC_MAX          LC_TIME
None.gif

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

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

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

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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
VS2019 添加bits/stdc++.h万能头文件库
VS2019 添加bits/stdc++.h万能头文件库
336 0
万能头文件#include“bits/stdc++.h”
万能头文件#include“bits/stdc++.h”
180 0
VS2010 无法打开源文件 stdio.h
  使用VS2010打开VS2013的CPP工程,出现上面的问题:无法打开源文件 stdio.h 解决方案是,打开下图右边的每个解决方案的属性,找到【配置属性】-【常规】-【平台工具集】, 将V120改为V100(VS2...
3826 0
std::fstream 中文路径
std::fstream 中文路径 eryar@163.com 用C++来开发管道出图程序IsoAlgo时,当PCF文件名中包含中文时,读取文件会失败。将下面数据存成一个简单文件:中文.txt  放到目标目录中来测试: Figure 1 包含中文的文件 简单测试程序代码如下所示: #i...
1478 0
codeblocks中添加-std=c99
早上用codeblocks编译一个c文件,出现这样一个编译错误: +'for'+loop+initial+declarations+are+only+allowed+in+C99+mode        原来codeblocks的gcc默认不以c99标准编译c文件,需要设置一下,具体如下: 1.
901 0
fstream,ifstream,ofstream 详解与用法
fstream,istream,ofstream 三个类之间的继承关系fstream :(fstream继承自istream和ofstream)1.typedef basic_fstream fstream;// 可以看出fstream就是basic_fstream2.
1391 0
转载:fstream和ifstream详细用法
文件 I/O 在C++中比烤蛋糕简单多了。在这篇文章里,我会详细解释ASCII和二进制文件的输入输出的每个细节,值得注意的是,所有这些都是用C++完成的。   一、ASCII 输出   为了使用下面的方法, 你必须包含头文件(译者注:在标准C++中,已经使用取代< fstream.h>,所有的C++标准头文件都是无后缀的。
893 0
+关注
杨粼波
网游的老兵
文章
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载