Click here to Skip to main content
15,870,165 members
Articles / Desktop Programming / MFC
Article

MFC CString::CompareNoCase does not work as expected

Rate me:
Please Sign up or sign in to vote.
4.25/5 (6 votes)
24 May 2002CPOL2 min read 230.1K   31   47
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Belgium Belgium
The first 5 years of my career I programmed in pure C. (Production automatisation software)
In Q4 of 1997 I switched to Visual C++/MFC. (Headend Management system for cable operators)
In Q1 of 2003 I changed job and started programming in JAVA (and Swing)
Later on I also worked in Adobe Flex, GWT and GXT.
Then, since our products became more web based, I started programming in JavaScript and Sencha ExtJS.
In my current job I program in Java, TypeScript and React.

Comments and Discussions

 
GeneralRe: Um... Pin
Joao Vaz27-May-02 7:15
Joao Vaz27-May-02 7:15 
GeneralRe: Um... Pin
Ravi Bhavnani27-May-02 7:13
professionalRavi Bhavnani27-May-02 7:13 
GeneralRe: Um... Pin
Joao Vaz27-May-02 7:26
Joao Vaz27-May-02 7:26 
GeneralRe: Um... Pin
Ravi Bhavnani27-May-02 7:17
professionalRavi Bhavnani27-May-02 7:17 
GeneralRe: Um... Pin
Joao Vaz27-May-02 7:30
Joao Vaz27-May-02 7:30 
GeneralRe: Um... Pin
Ravi Bhavnani27-May-02 7:29
professionalRavi Bhavnani27-May-02 7:29 
GeneralRe: Um... Pin
Joao Vaz27-May-02 7:47
Joao Vaz27-May-02 7:47 
GeneralRe: Um... Pin
Ravi Bhavnani27-May-02 7:46
professionalRavi Bhavnani27-May-02 7:46 
Joao Vaz wrote:
Heh , a Java convert, hein ?
Not really, my first love is (and has always been) Windows. But I found some nice concepts in Java when I used it for about 18 months. Now it's on to C# and .Net of which I know absolutely nothing!

Joao Vaz wrote:
your site is full of humor
The product of a warped mind. Smile | :)

/ravi

Let's try putting "civil" back into "civilization"
http://www.ravib.com
ravib@ravib.com
GeneralRe: Um... Pin
27-May-02 20:29
suss27-May-02 20:29 
GeneralRe: Um... Pin
Zac Howland3-Feb-03 5:25
Zac Howland3-Feb-03 5:25 
GeneralRe: Um... Pin
Joao Vaz3-Feb-03 6:06
Joao Vaz3-Feb-03 6:06 
GeneralRe: Um... Pin
Zac Howland3-Feb-03 8:18
Zac Howland3-Feb-03 8:18 
GeneralRe: Um... Pin
Joao Vaz4-Feb-03 0:09
Joao Vaz4-Feb-03 0:09 
GeneralRe: Um... Pin
28-May-02 0:32
suss28-May-02 0:32 
GeneralRe: Um... Pin
Ravi Bhavnani28-May-02 6:00
professionalRavi Bhavnani28-May-02 6:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.