碰到的問(wèn)題: 今天碰到這個(gè)問(wèn)題了,是在使用Crypto++庫(kù)的時(shí)候遇到的,該庫(kù)操作文件是使用的std::ifstream。在我給文件生成簽名的時(shí)候,每每碰到中文路徑名就出錯(cuò),后來(lái)跟進(jìn)庫(kù)代碼一看是打開(kāi)文件的時(shí)候出錯(cuò)。
據(jù)說(shuō)這個(gè)問(wèn)題在VS2003以及之前版本是沒(méi)有的,不幸的是我現(xiàn)在用的是VS2005的版本。
產(chǎn)生問(wèn)題的原因: 究竟是因?yàn)槭裁串a(chǎn)生的這個(gè)問(wèn)題呢?如果你跟進(jìn)去VC實(shí)現(xiàn)版的STL代碼,你會(huì)發(fā)現(xiàn),它有一個(gè)將傳入的char字符串文件名轉(zhuǎn)換為UNICODE的wchar_t字符串這樣一個(gè)過(guò)程,其代碼如下:
_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方法最終進(jìn)入到了_mbstowcs_l_helper方法,
如果得到的是C locale,則它認(rèn)為傳進(jìn)來(lái)的字符串為ASCII碼,也就是單字節(jié)字符,它僅僅是進(jìn)行了char到wchar_t指針的轉(zhuǎn)換而已,那很顯然第二個(gè)字節(jié)肯定為零,自然的字符就錯(cuò)了;
如果不是的話,它認(rèn)為是多字節(jié)字符,將會(huì)調(diào)用MultiByteToWideChar進(jìn)行轉(zhuǎn)碼。
在VC8里面,local默認(rèn)是C locale,所以就出錯(cuò)了。以下為摘抄的該段代碼:
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 */
知道了問(wèn)題的緣由,才能夠更好的解決問(wèn)題。在VC6和VC7都沒(méi)有經(jīng)過(guò)這個(gè)步驟,好像是直接調(diào)用的SDK的CreateFile方法,因此就沒(méi)有問(wèn)題。而VC8這樣根據(jù)locale來(lái)轉(zhuǎn)碼所以造成了問(wèn)題。
解決辦法:
1、使用C語(yǔ)言的函數(shù)設(shè)置為中文運(yùn)行環(huán)境
setlocale(LC_ALL, "Chinese-simplified");
其中參數(shù)一有以下幾個(gè)值:

/**//* 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當(dāng)中查到。
從上面所貼出來(lái)的代碼可以知道wbstowcs_s方法依賴著locale的LC_CTYPE分類(lèi)的數(shù)值。
2、使用STL函數(shù)設(shè)置為系統(tǒng)語(yǔ)言環(huán)境
std::locale::global(std::locale(""));
建議用這個(gè)方法,因?yàn)楦麮++一些,而且可以很容易的恢復(fù)之前的locale,以下會(huì)說(shuō)到這個(gè)。
最終的解決方法:經(jīng)過(guò)了我的測(cè)試,兩種方法都是可用的。
但是,接著又有問(wèn)題出現(xiàn)了,std::cout輸出中文時(shí)候,中文是輸出不了的!這可真是令人煩擾了。要解決這個(gè)問(wèn)題那么就只能是將代碼頁(yè)再設(shè)置回去了,用以下方法可以很好解決問(wèn)題。
//設(shè)置代碼頁(yè)為簡(jiǎn)體中文,936是簡(jiǎn)體中文的代碼頁(yè)。
std::locale loc1 = std::locale::global(std::locale(".936"));


{
// 在這里使用std::ifstream 或者 std::fstream
}

//恢復(fù)原來(lái)的代碼頁(yè)
std::locale::global(std::locale(loc1));
這個(gè)恐怕是我現(xiàn)在能夠得到的最佳方法了。
參考資料:
C語(yǔ)言locale介紹: http://www.cnblogs.com/floerggyy/archive/2008/04/15/1154738.htmlVC2005(英文版)處理文件名為中文的文件: http://hi.baidu.com/ark803/blog/item/9d54661f4ff25866f724e470.html