碰到的問題: 今天碰到這個問題了,是在使用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.htmlVC2005(英文版)處理文件名為中文的文件: http://hi.baidu.com/ark803/blog/item/9d54661f4ff25866f724e470.html