前两天在处理一个字符串的时候发现一个奇怪的现象:当删除字符串中的引号时,后面的中文都变成了乱码,搞得我一头雾水。
一看内存,发现中文字符的前后两个字节被分了家:一个字节岿然不动,一个字节根据删除减少的字符数向前移动。
这就奇怪了,难道是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。