Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

MFC CString::CompareNoCase does not work as expected

0.00/5 (No votes)
24 May 2002 1  
CString::CompareNoCase depends on locale, even though the docs say otherwise.

Introduction

Recently I found a program of mine doing all kind of unexpected things because of CString::CompareNoCase() not returning what I expected it to return.

I live in Belgium where in the northern part of the country they speak Dutch while in the southern part they speak French. My application was being used in the latter part. So the users provided names containing the characters: �, �, �,.  Somewhere in my app I used the given name to search the corresponding object in a list. Since the given names had to be (case insensitive) unique I stored those names in uppercase format.

Afterwards I compared (in a case insensitive way) the given name with the uppercase name of each object in the list.
And that went unexpectedly wrong! Look at the following code snippet:

CString strText("�l�ve - � la fa�on - ch�teau"); // French indeed


CString strTextUpper(strText);
strTextUpper.MakeUpper(); // Upper case the original 


CString strTextLower(strTextUpper);
strTextLower.MakeLower(); // Lower case the upper cased original


CString strMsg;
if ( strTextUpper.CompareNoCase(strText) )
        strMsg.Format("Original text: <%s>\n"
                      "Uppered: <%s>\n"
                      "CompareNoCase says they differ!\n"
                      "Lowered again: <%s>\n\n"
                      "Conclusion: CompareNoCase doesn't work correctly!",
                      strText, strTextUpper, strTextLower);
else
        strMsg.Format("Original:%s\nUppered:%s\nLowered again:%s\n\n"
                      "Conclusion: CompareNoCase works as expected!",
                      strText, strTextUpper, strTextLower);
AfxMessageBox(strMsg);
The output is:

So

  • I ask to uppercase a CString object
  • I ask to compare it (case insensitive) with the original one
  • Result: they differ!!!
While for CString::CompareNoCase I read:

Compares this CString object with another string using the generic-text function _tcsicmp. The generic-text function _tcsicmp, which is defined in TCHAR.H, maps to either _stricmp, _wcsicmp, _mbsicmp depending on the character set that is defined at compile time. Each of these functions performs a case-insensitive comparison of the strings, and is not affected by locale.

While digging deeper in MSDN I discovered the existence of CString::CollateNoCase for which I read:

Compares this CString object with another string using the generic-text function _tcscoll. The generic-text function _tcscoll, which is defined in TCHAR.H, maps to either stricoll, wcsicoll, or _mbsicoll depending on the character set that is defined at compile time. Each of these functions performs a case-insensitive comparison of the strings, according to the code page currently in use.

and for �_strupr, _wcsupr, _mbsupr� I read:

The _strupr function converts, in place, each lowercase letter in string to uppercase. The conversion is determined by the LC_CTYPE category setting of the current locale. Other characters are not affected. For more information on LC_CTYPE, see setlocale.

Workaround

This knowledge led me to two workarounds:
  1. Use CString::CollateNoCase instead of CompareNoCase:

    Result:

  2. Although the help says it works independent of the locale, before calling CString::CompareNoCase set (the LC_CTYPE part of the) the locale. e.g.:
    setlocale(LC_CTYPE, "french-belgian");
    Result:

     

I don�t know if you agree with me, but I find this rather unnatural. I call this a bug:

  • You ask MFC to convert a CString to its uppercase variant (Man, you don�t care what locale rules it applies!)
  • You ask MFC to compare the CString it just uppercased (using �some� rules) with the original but in a case insensitive way
    (so in fact you expect it to neglect the uppercase variant and to use the original)
  • and MFC kindly tells you they differ! (:surprised:)

Conclusion

My confidence in MFC�s CString comparison functions isn�t great after I discovered this. And that�s why I wrote my own ever-working workaround:

int MyCompareNoCase(LPCTSTR str1, LPCTSTR str2)
{
  CString strTmp1(str1);
  CString strTmp2(str2);
  strTmp1.MakeUpper();
  strTmp2.MakeUpper();
  return strTmp1.Compare(strTmp2);
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here