剖析ifstream開啟含中文路徑名檔案失敗的原因

來源:互聯網
上載者:User

最近寫程式的時候遇到了使用ifstream開啟含中文路徑檔案時失敗的問題,在網上翻了一下,發現這是一個普遍遇到的問題,在很多人的博文中也都給出了一些解決技巧,但大多是轉載的東西,很少對這個問題引發的原因有一個清晰、全面的解釋。因此,我覺得有必要對該問題引發的原因作一個詳細的剖析,希望對遇到同樣問題的朋友們能有所協助。

首先,用一個簡單的例子來重現一下我所遇到的問題:

(1)在VS2008的“Property  Pages”屬性頁面中,選擇“Configuration Properties”-->“General”,可以看到當前使用的字元集是“Multi-Byte Character Set”,也就是說程式中使用的是多位元組字元集。

 

                  

(2)接下來看看ifstream開啟txt檔案的簡單代碼:

[cpp]
view plaincopy
  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")),便不會有開啟檔案不成功的問題了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.