剖析ifstream打开含中文路径名文件失败的原因
http://blog.csdn.net/yukin_xue/article/details/7543423
最近写程序的时候遇到了使用ifstream打开含中文路径文件时失败的问题,在网上翻了一下,发现这是一个普遍遇到的问题,在很多人的博文中也都给出了一些解决技巧,但大多是转载的东西,很少对这个问题引发的原因有一个清晰、全面的解释。因此,我觉得有必要对该问题引发的原因作一个详细的剖析,希望对遇到同样问题的朋友们能有所帮助。
首先,用一个简单的例子来重现一下我所遇到的问题:
(1)在VS2008的“Property Pages”属性页中,选择“Configuration Properties”-->“General”,可以看到当前使用的字符集是“Multi-Byte Character Set”,也就是说程序中使用的是多字节字符集。

(2)接下来看看ifstream打开txt文件的简单代码:
- #include "stdafx.h"
- #include <fstream>
- #include <iostream>
- using namespace std;
- int _tmain(int argc, _TCHAR* argv[])
- {
- ifstream infile("d://测试.txt");
- if(infile.is_open())
- {
- cout<<"Open Success!";
- }
- else
- {
- cout<<"Open Fail!";
- }
- return 0;
- }
(3)运行结果:输出“Open Fail” (打开文件失败!)
从设置选项中可以看到,工程中使用的字符集可设置为“Multi-Byte Character Set”或“Unicode Character Set”,其中“Multi-Byte Character Set”表示使用ANSI编码方式,“Unicode Character Set”表示使用UNICODE编码方式。
那么这两种编码方式有什么样的区别呢?
(1)传统的计算机使用ANSI编码,在ANSI编码模式下,英文字符都用1个字节表示,而某些其它国家的文字(如汉字、日文),无法用单个字节来表示,ANSI便采用多个字节来表示这些字符(汉字是2个字节)。
(2)UNICODE包含UTF-8、UTF-16、UTF-32等多种编码方案(目前windows一般使用UTF-16)。拿UTF-16来说,规定所有字符都使用2个字节表示(不论英文字母还是汉字),对于超出2个字节范围的字符采用代理(采用4个字节表示)。
UNICODE相比ANSI有很多方面的优势(优势体现在哪?),微软非常提倡使用UNICODE编码方式,在MS较新版本的系统中都是采用UNICODE编码的。因此,即便我们在自己写的程序中使用了ANSI编码,系统会将其转换为UNICODE再对其进行处理。
接下来我们说一下ifstream。在调用ifstream的open方法时,系统内部调用mbstowcs_s进行文件名转换(mbstowcs_s函数的作用是把多字节字符转化为宽字符),需要注意的是,该函数的调用结果依赖于程序的本地化设置(什么是本地化设置?)。而本地化设置可以通过setlocale函数来设置,譬如:setlocale(LC_ALL, "chinese")表示将程序本身的语言设置为中文,而程序启动时默认设置为LC_ALL="C"。在使用mbstowcs_s进行字符串转换时,只有当LC_ALL="chinese"时,含中文的字符串才能正确的转换成其对应的宽字节字符,否则(在LC_ALL="C"时),汉字会被看成2个单字节的字符,然后再转换成宽字节的字符,这样转换的结果显然是错误的!这就是ifstream打开含中文路径的文件失败的原因,因为"d://测试.txt"转换后得到错误的路径,因此文件打不开!
解决方法如下:
1: /********************************************************************
2: created: 2008/05/10
3: created: 10:5:2008 23:56
4: filename: k:/sj/fstreamTest/fstreamTest/main.cpp
5: file path: k:/sj/fstreamTest/fstreamTest
6: file base: main
7: file ext: cpp
8: author: Gohan
9: *********************************************************************/
10: #include <tchar.h>
11: #include <fstream>
12: #include <iostream>
13: using namespace std;
14: int main()
15: {
16: /************************************************************************/
17: /* 方法1,使用_TEXT()宏定义将字符串常量指定为TCHAR*类型 */
18: /* 如果是我,首选此类型 */
19: /************************************************************************/
20: fstream file;
21: file.open(_TEXT("c://测试//测试文本.txt"));
22: cout<<file.rdbuf();
23: file.close();
24:
25: /************************************************************************/
26: /* 方法2,使用STL中的locale类的静态方法指定全局locale */
27: /* 使用该方法以后,cout可能不能正常输出中文,十分蹊跷 */
28: /* 我发现了勉强解决的方法:不要在还原区域设定前用cout或wcout 输出中文 */
29: /* 否则后果就是还原区域设定后无法使用cout wcout输出中文 */
30: /************************************************************************/
31: locale::global(locale(""));//将全局区域设为操作系统默认区域
32: file.open("c://测试//测试文本2.txt");//可以顺利打开文件了
33: locale::global(locale("C"));//还原全局区域设定
34: cout<<file.rdbuf();
35: file.close();
36:
37: /************************************************************************/
38: /* 方法3,使用C函数setlocale,不能用cout输出中文的问题解决方法同上 */
39: /************************************************************************/
40: setlocale(LC_ALL,"Chinese-simplified");//设置中文环境
41: file.open("c://测试//测试文本3.txt");//可以顺利打开文件了
42: setlocale(LC_ALL,"C");//还原
43: cout<<file.rdbuf();
44: file.close();
45: }
参见博客:http://www.cppblog.com/gohan/archive/2008/05/11/49488.html
由于windows提倡使用UNICODE编码,因此,我们在使用VS编写程序的时候,最好也都使用UNICODE字符集。这样有利于避免字符集转换带来的问题,同时,也有利于提高效率(前面提到,windows内部会把ANSI编码转换为UNICODE再处理,这些转换当然也带来了额外的时间消耗)。
在示例的程序中,可以将工程字符集设置为UNICODE,然后将字符串前面加上_T(这样,在字符集已经设置为UNICODE的情况下,该字符串会自动采用宽字符表示),例如:ifstream infile(_T("d://测试.txt")),便不会有打开文件不成功的问题了。
剖析ifstream打开含中文路径名文件失败的原因的更多相关文章
- java中File的delete()方法删除文件失败的原因
java中File的delete()方法删除文件失败的原因 学习了:http://hujinfan.iteye.com/blog/1266387 的确是忘记关闭了: 引用原文膜拜一下: 一般来说 ja ...
- sublime text2 打开包含中文的文件会自动追加.dump后缀解决办法
用sublime text2 打开.c, .h,.txt等文件会自动追加一个.dump后缀,這样在打开.c,.h等文件时无法正常识别,从而无法正常进行语法着色,网上说是因为安装了GBK Encodin ...
- myeclipse 保存含中文的jsp失败,提示内容含有 ISO-8859-1 不支持的字符
就是这货,网上说各种设置首选项编码神马的,但我只是临时学一学jsp,装的myeclipse貌似不全,没有他们说的选项,后来发现了解决方案: 出错是因为我的jsp文件是用于在其他jsp中引入的,所以没有 ...
- php ci框架中载入css和js文件失败的原因及解决方法
在将html页面整合到ci框架里面的时候,载入css和js失败. 原因是ci框架是入口的框架 对框架中文件的全部请求都须要经过index.php处理完毕,当载入外部的css和js文件的时候要使 用ba ...
- 用adb pull命令从android系统中读取文件失败的原因及解决办法
问题:使用adb pull命令从android系统中读取文件失败.显示:Permission denied 原因:是由于文件权限原因引起. 使用ls -l命令查看android系统中的 ...
- 解决:R读取含中文excel文件,read.xlsx乱码问题
1.新建testexcel.xlsx文件 2.创建R文件:test.R # 定义文件变量 excel_path <- "chapter2/testexcel.xlsx" # ...
- 转!!java中File的delete()方法删除文件失败的原因
一般来说 java file.delete失败 有以下几个原因 1.看看是否被别的进程引用,手工删除试试(删除不了就是被别的进程占用)2.file是文件夹 并且不为空,有别的文件夹或文件, 3.极有可 ...
- fopen打开文件失败的问题
fopen打开带中文路径或含中文名称的文件失败. 解决这个问题有两个方法:一是改用_wfopen,这个函数接受两个宽字符类型,函数原型如下: FILE* _wfopen(const wchar_t* ...
- Mysql load data infile 命令导入含中文csv源数据文件 【错误代码 1300】
[1]Load data infile 命令导入含中文csv源数据文件 报错:Invalid utf8 character string: '??֧' (1)问题现象 csv格式文件源数据: 导入SQ ...
随机推荐
- loj2053 「HNOI2016」大数
ref #include <algorithm> #include <iostream> #include <cstring> #include <cstdi ...
- VMware workstation 9.0中安装Windows server 2012 和 Hyper-v
一.准备工作 首先下载和安装和安装VMware workstation 9.0 下载Windows server 2012 iso镜像文件 二.安装Windows server 2012虚拟机 1.新 ...
- Careercup - Microsoft面试题 - 5917873302142976
2014-05-12 06:56 题目链接 原题: A link list contains following elements struct node{ int data; node* next; ...
- Wordpress 作者模板页中的自定义帖子类型分页问题
<?php // 获取当前页面的页数,函数的参数为 paged $paged = (get_query_var('paged')) ? get_query_var('paged') : 1; $ ...
- hdu 4183 EK最大流算法
欢迎参加——每周六晚的BestCoder(有米!) Pahom on Water Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 327 ...
- 用日志记录Linux用户执行的每一条命令(history)
工作中,需要把用户执行的每一个命令都记录下来,并发送到日志服务器的需求,为此我做了一个简单的解决方案.这个方案会在每个用户退出登录 时把用户所执行的每一个命令都发送给日志守护进程rsyslogd,你也 ...
- 使用SS5搭建linux下的sock5代理服务器
http://sourceforge.net/projects/ss5/下载最新源码 apt-get install libpam0g-dev apt-get install libldap2-dev ...
- 【距离GDOI:141天】 滚入数位DP的坑
作为博客园的第一篇...我都不知道要写什么了 ... 其实今天很没状态,就当吐槽吧... 嗯,被黄神带去写treap+可持久化线段树,然后在可持久化的删除上面跪了两天,真的是一跪不起.我已经连续多久没 ...
- [CQOI2014][bzoj3507] 通配符匹配 [字符串hash+dp]
题面 传送门 思路 0x01 KMP 一个非常显然而优秀的想法:把模板串按照'*'分段,然后对于每一段求$next$,'?'就当成可以对于任意字符匹配就行了 对于每个文本串,从前往后找第一个可以匹配的 ...
- [luoguP2596] [ZJOI2006]书架(splay)
传送门 题目中的几个操作,直接splay搞一下即可: 把s旋转到根,左子树接到右子树 把s旋转到根,右子树接到左子树 交换s相邻的信息即可 把s旋转到根,左子树的大小即为答案 找第k大 没了 #inc ...