前兩天在處理一個字符串的時候發現一個奇怪的現象:當刪除字符串中的引號時,後面的中文都變成了亂碼,搞得我一頭霧水。
一看內存,發現中文字符的前後兩個字節被分了家:一個字節巋然不動,一個字節根據刪除減少的字符數向前移動。
這就奇怪了,難道是MFC的問題??
跟進去一看,果不其然! MFC42 (vs6)是這麼處理的:
CString::Remove(TCHAR ch)
......
while (pstrSource < pstrEnd)
{
if (*pstrSource != chRemove)
{
*pstrDest = *pstrSource;
pstrDest = _tcsinc(pstrDest);
}
pstrSource = _tcsinc(pstrSource);
}
MFC42其實考慮到了MBCS的情況, 所以用了 _tcsinc來判斷下一字符的位置;可惜只考慮了一半,賦值時
*pstrDest = *pstrSource
可不像它想象的那麼聰明,只能根據類型定義拷貝了單個字節。 所以就發生了刪除一個字符後,後面的字符往前移動時只移動了雙字節字符的前一個字節的情況。
根據源代碼的意思,實際上刪除任何一個字符,其後面的中文字符串都會亂掉。
字符串的處理是個蠻古董的話題了,出現這樣子的bug確實讓我吃了一驚。剛好手頭還有vs2003, 於是試了試,發現已經更改過來了。
代碼如下:
while( pszSource < pszEnd )
{
PXSTR pszNewSource = StringTraits::CharNext( pszSource );
if( *pszSource != chRemove )
{
// Copy the source to the destination. Remember to copy all bytes of an MBCS character
PXSTR pszNewDest = pszDest+(pszNewSource-pszSource);
while( pszDest != pszNewDest )
{
*pszDest = *pszSource;
pszSource++;
pszDest++;
}
}
pszSource = pszNewSource;
}
可以看到MFC(7.1)特地用了一個while循環來處理多字節字符的情況。這次,MFC(7.1)記起來了,還在代碼里加了註釋。它說:
“Remember to copy all bytes of an MBCS character”
我想看看msdn上對於這個bug的說法, 於是google了一把 “CString Remove Bug Site:microsoft.com”。
這一Google沒發現Remove的說明, 卻又發現了Right, Left, Mid的bug說明:
KB810448:FIX: CString Functions Right, Left, and Mid May Cause Access Violation.
文章最後說明,該bug存在於MFC42(visul studio 6), Visual C++ .NET 2002 Standard Edition中。
有了前次的經歷,Right, Left, Mid在MFC42中有問題就不那麼嚇人樂,不過在vs2002中還存在,就有點......
由於沒找到關於 CString::Remove 的 bug說明,因此寫了這篇文章,提醒還在使用 vs2003之前版本的朋友們,慎用 CString 的 Remove, Right, Left, Mid。