Comparing case insensitive char array.

Hi everyone.
I was searching for comparing string case insensitively, from IBM website I found strcmpi()function compares and working perfectly in DEV c++ but not in CodeBlocks.
It shows error that strcmpi() was not declared in this scope.

here is the link where I read about this function
https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/rtref/strcmpi.htm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  #include<iostream>
#include<string.h>
using namespace std;
int main()
{
	char str1 [50];
	char str2 [50];

	strcpy(str1,"The GAmE");


	strcpy(str2,"The Game");

	if(strcmpi(str1, str2) == 0)
	{
		cout<<"Both the strings are equal";
	}
	else
	{
		cout<<"Both the strings are not equal";
	}
}


Can you please help me out..
If you read that IBM page, you will see that it specifies "Language level: Extension". That means the function is a non standard extension and is not part of the C++ standard. Therefore it is not portable across implementations.

You might have better luck with the strncasecmp function. Again a non-standard C++ extension, but part of the XPG4 standard which you might find more widely implemented.

unfortunatly strncasecmp () didn't work either.

is there any way If I copy include folder from MinGW of DEV c++ to CodeBlocks MinGW folder or just copy header files and rename it and then include that library. Would it work?

just do it yourself. One quick try at it:

1
2
3
4
5
6
7
8
9
10
11
bool strcmp2(char* a, char *b)
{
    int la = strlen(a);
    int lb = strlen(b);
    if(la != lb) return false;
   for(lb = 0; lb < la; lb++)
   {
       if(toupper(a[lb] != toupper(b[lb]) return false;
   }
    return true;
}



Am I doing something wrong?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<iostream>
#include<string.h>
using namespace std;

bool strcmp2(char* a, char *b)
{
    int la = strlen(a);
    int lb = strlen(b);
    if(la != lb) return false;
   for(lb = 0; lb < la; lb++)
   {
       (toupper(a[lb] != toupper(b[lb]))) return false;
   }
    return true;
}

int main()
{
	char str1 [50];
	char str2 [50];

	strcpy(str1,"The GAmmer");


	strcpy(str2,"The Game");

	if (strcmp2(str1,str2) == 0)
	cout<<"String is equal ";
	else
	cout<<"Not equal";

}

output:
String is equal
you removed the if from the for loop
this wouldn't even compile for me.
and, it looks like *I* missed a ) in the loop.
it should be

if(toupper(a[lb]) != toupper(b[lb])) return false;


also 0 is false, your text is reversed.
For clarity, use the words true and false is highly recommended, eg
if(strcmp2(str1, str2) == false)
not equal
else
equal

Now that I think about it, the original strcmp is 0 on true. You can reverse the true and false in the function to get this if you prefer.
Last edited on
A more efficient implementation might look like the following:

1
2
3
4
5
6
7
8
9
# include <cctype> 

int my_stricmp(char const* a, char const* b) {
  // find first differing character between the C-strings a and b  
  while (*a && std::toupper(*a) == std::toupper(*b))
    ++a, ++b;
  
  return *a - *b;
}

Note that std::strcmp doesn't return a bool.
http://en.cppreference.com/w/cpp/string/byte/strcmp
Last edited on
No. Handling case correctly is actually really, really hard. The reason those functions are so obscure is because they are from an era when ASCII/Latin-1 was all that mattered. Today better options exist, at the expense of a large library.

For what you want, just write a function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <cctype>
#include <ciso646>
#include <cstring>

int strcmpi( const char* a, const char* b, const std::locale loc = std::locale() )
{
  while (*a or *b)
  {
    char c = std::toupper( *a++, loc );
    char d = std::toupper( *b++, loc );
    if (c < d) return -1;
    if (c > d) return  1;
  }
  return 0;  
}

Since you are using C++, you might as well be using strings for all your text data needs. In which case, you can use this nifty block of code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <algorithm>
#include <cctype>
#include <ciso646>
#include <iterator>
#include <string>

// Here's our Case Insensitive type...
struct ci
{
  const std::locale& loc;
  const std::string& s;
  ci( const std::string& s, const std::locale& loc = std::locale() ): loc(loc), s(s) { }

  // ...and the core comparison functions...
  struct lt { const std::locale &aloc, &bloc; bool operator () ( char a, char b ) const { return std::tolower( a, aloc ) <  std::tolower( b, bloc ); } };
  struct eq { const std::locale &aloc, &bloc; bool operator () ( char a, char b ) const { return std::tolower( a, aloc ) == std::tolower( b, bloc ); } };
  struct gt { const std::locale &aloc, &bloc; bool operator () ( char a, char b ) const { return std::tolower( a, aloc ) >  std::tolower( b, bloc ); } };
  
  template <typename Predicate1, typename Predicate2>
  static bool compare( 
    const std::string& a, const std::locale& aloc,
    const std::string& b, const std::locale& bloc,
    Predicate1 p1,        Predicate2 p2           )
  {
    auto n = std::min( a.size(), b.size() );
    auto m = std::mismatch( a.begin(), std::next( a.begin(), n ), b.begin(), ci::eq{ aloc, bloc } );
    return (m.first == a.end()) ? p1( a.size(), b.size() ) : p2( *m.first, *m.second );
  }
};

// ...and comparison operators
bool operator == ( const ci& a, const ci& b ) { return a.compare( a.s, a.loc, b.s, b.loc, []( auto a, auto b ) { return a == b; }, ci::eq{ a.loc, b.loc } ); }
bool operator <  ( const ci& a, const ci& b ) { return a.compare( a.s, a.loc, b.s, b.loc, []( auto a, auto b ) { return a <  b; }, ci::lt{ a.loc, b.loc } ); }
bool operator >  ( const ci& a, const ci& b ) { return a.compare( a.s, a.loc, b.s, b.loc, []( auto a, auto b ) { return a >  b; }, ci::gt{ a.loc, b.loc } ); }
bool operator != ( const ci& a, const ci& b ) { return !(a == b); }
bool operator <= ( const ci& a, const ci& b ) { return !(a >  b); }
bool operator >= ( const ci& a, const ci& b ) { return !(a <  b); }

bool operator == ( const std::string& a, const ci& b ) { return ci(a) == b; }
bool operator <  ( const std::string& a, const ci& b ) { return ci(a) <  b; }
bool operator >  ( const std::string& a, const ci& b ) { return ci(a) >  b; }
bool operator != ( const std::string& a, const ci& b ) { return ci(a) != b; }
bool operator <= ( const std::string& a, const ci& b ) { return ci(a) <= b; }
bool operator >= ( const std::string& a, const ci& b ) { return ci(a) >= b; }

bool operator == ( const ci& a, const std::string& b ) { return a == ci(b); }
bool operator <  ( const ci& a, const std::string& b ) { return a <  ci(b); }
bool operator >  ( const ci& a, const std::string& b ) { return a >  ci(b); }
bool operator != ( const ci& a, const std::string& b ) { return a != ci(b); }
bool operator <= ( const ci& a, const std::string& b ) { return a <= ci(b); }
bool operator >= ( const ci& a, const std::string& b ) { return a >= ci(b); }

With this, you can easily add simple case-insensitivity to any string comparison:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{
  std::string name1, name2;

  std::cout << "Favorite person's name? ";  getline( std::cin, name1 );
  std::cout << "Your name? ";               getline( std::cin, name2 );

  if (ci(name1) == name2)
    std::cout << "Bigly!\n";

  else if (ci(name1) == "sue")
    std::cout << "Kiss'er once for me. (Or punch the dirty, mangy dog that named him \"Sue\".)\n";

  else std::cout << "Howdy " << name1 << "!\n";
}

Enjoy!
that's a good point mbozzi ... using the zero terminator as part of the comparison and elimination of the lengths which is an extra iteration over the bytes.
agreed, yours is better.

I know it didn't return a bool, but he wants a binary answer. C didn't have bool at that time!
C didn't have bool at that time

Good point! I mean that it doesn't return zero/nonzero - it returns positive/zero/negative depending on the relative ordering of the two strings.
Last edited on
Thanks, jonnin Duthomhas mbozzi
And Sorry mbozzi I didn't understand what you said. it's quite complicated for me.
mbozzi is talking about the way that strcmp() is supposed to work with respect to its return value. When you use it, you use the comparison operator you would like to put between the strings between the function call and a zero instead:

1
2
3
4
5
  if (strcmpi( a, b ) < 0)  // a < b
  if (strcmpi( a, b ) <= 0)  // a <= b
  if (strcmpi( a, b ) == 0)  // a == b
  if (strcmpi( a, b ) > 0)  // a > b
  ...

alright, thats making sense now..
Topic archived. No new replies allowed.