zoukankan      html  css  js  c++  java
  • 剖析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文件的简单代码:

    1. #include "stdafx.h"  
    2. #include <fstream>  
    3. #include <iostream>  
    4. using namespace std;  
    5. int _tmain(int argc, _TCHAR* argv[])  
    6. {  
    7.     ifstream infile("d://测试.txt");  
    8.     if(infile.is_open())  
    9.     {  
    10.         cout<<"Open Success!";  
    11.     }  
    12.     else  
    13.     {  
    14.         cout<<"Open Fail!";  
    15.     }  
    16.     return 0;  
    17. }  

    (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")),便不会有打开文件不成功的问题了。

  • 相关阅读:
    Alpha版与Beta版
    URL的概念
    正则表达式(即:匹配模式Pattern)
    前置与后置的递增/递减操作符
    【平板电脑模拟器】PC端-Chrome自带的功能
    【HOSTS相关】前端提供测试模板地址
    【netlimiter】的使用
    <转>window.event对象在ie与ff中的区别
    <转>全面理解javascript的caller,callee,call,apply概念(修改版)
    document.styleSheets以及js中实现样式表的切换、样式的增加减少
  • 原文地址:https://www.cnblogs.com/kex1n/p/4150938.html
Copyright © 2011-2022 走看看