このページの解説は C99 をベースとしています。
以下は目次です。
アルファベットの大文字と小文字の違いを無視して、文字列比較を行いたいとします。
標準ライブラリの strcmp関数と同じ感覚で使える方が便利でしょう。引数や戻り値の仕様を同じにして、独自の strcmp_ignorecase関数を作ります。たとえば、以下の printf関数の呼び出しのそれぞれが、コメントに示した結果を出力することを目指します。
("%d\n", strcmp_ignorecase("abc", "abc")); // 0
printf("%d\n", strcmp_ignorecase("abc", "ABC")); // 0
printf("%d\n", strcmp_ignorecase("abc", "Abc")); // 0
printf("%d\n", strcmp_ignorecase("abc", "ABc")); // 0
printf
("%d\n", strcmp_ignorecase("abc", "axC")); // 0 より小さい
printf("%d\n", strcmp_ignorecase("axc", "abC")); // 0 より大きい
printf
("%d\n", strcmp_ignorecase("abcd", "ABc")); // 0 より大きい
printf("%d\n", strcmp_ignorecase("abc", "ABcd")); // 0 より小さい
printf("%d\n", strcmp_ignorecase("", "ABc")); // 0 より小さい
printf("%d\n", strcmp_ignorecase("abc", "")); // 0 より大きい
printf("%d\n", strcmp_ignorecase("", "")); // 0 printf
残念ながら、標準ライブラリ関数に一発で目的を達する関数はありません。そのため、自力で実装することが多いです。
方法としては、それぞれの文字列を1文字ずつ、小文字(あるいは大文字)のどちらかに合わせてから比較することを繰り返します。ここでは小文字に合わせることにします。
ただし、ASCIIコードにおいては、A~Z と a~z の間に([, \, ], ^, _, `)があるため、これらの文字が含まれている場合には、大文字と小文字のどちらに統一するかによって、結果に違いが生まれてきます。つまり、‘a’ と ‘^’ の比較が、‘a’ と ‘^’ として行われれば ‘^’ の方が小さいですし、‘A’ と ‘^’ として行われれば ‘^’ の方が大きいことになります。大文字の方が、数値(文字コード)としては小さいので、このような結果になります。「一致するかしないか」にしか興味がないケースが多く、その場合は特に問題ありませんが、本当に大小関係を知る必要がある場合には注意が必要です。
アルファベットを小文字化するには、標準ライブラリ関数の tolower関数を使用します。「逆引き 文字列を大文字化・小文字化する」の方法を使って、文字列全体を大文字や小文字に統一できますが、これは、元の文字列を壊してしまうため好ましくありません。
#include <ctype.h>
#include <stdio.h>
/*
文字列を大文字・小文字の違いを無視して比較する
s1: 比較する文字列
s2: 比較する文字列
戻り値: s1 の方が小さい場合は 0 より小さい値、
s1 の方が大きい場合は 0 より大きい値、
s1 と s2 が一致する場合は 0 を返す。
*/
int strcmp_ignorecase(const char* s1, const char* s2)
{
// 1文字ずつ、小文字に統一して比較する。
// この比較が真になり続けた場合、2つの文字列は一致する。
while (tolower(*s1) == tolower(*s2)) {
// s1とs2 が同時に '\0' に到達したのなら、2つの文字列は一致している。
// 一方が先に '\0' に到達するケースでは、while文の条件式を満たさなくなるため、
// while文から脱出している。
if (*s1 == '\0') {
return 0;
}
++s1;
++s2;
}
// 一致しなくなった文字同士で比較して、
// s1 の側が小さいなら 0 より小さい値を、s1 の側が大きいなら 0 より大きい値を返す。
return tolower(*s1) - tolower(*s2);
}
int main(void)
{
("%d\n", strcmp_ignorecase("abc", "abc"));
printf("%d\n", strcmp_ignorecase("abc", "ABC"));
printf("%d\n", strcmp_ignorecase("abc", "Abc"));
printf("%d\n", strcmp_ignorecase("abc", "ABc"));
printf
("%d\n", strcmp_ignorecase("abc", "axC"));
printf("%d\n", strcmp_ignorecase("axc", "abC"));
printf
("%d\n", strcmp_ignorecase("abcd", "ABc"));
printf("%d\n", strcmp_ignorecase("abc", "ABcd"));
printf("%d\n", strcmp_ignorecase("", "ABc"));
printf("%d\n", strcmp_ignorecase("abc", ""));
printf("%d\n", strcmp_ignorecase("", ""));
printf}
実行結果:
0
0
0
0
-22
22
68
-68
-65
65
0
比較する文字列の長さが同じであるとは限らないため、意外と実装は難しくなります。
2つの文字列を先頭から1文字ずつ、小文字化して比較することを繰り返します。両者が ‘\0’ に到達するまでの間、ずっと一致し続けた場合は、両者は一致しますから 0 を返せば良いです。
途中で ‘b’ と ‘x’ のように、一致しない文字が現れた場合、どちらが小さいか判断して戻り値を返します。
このとき、if文を使って大小比較をしてもいいですし、減算で実装してもいいです。strcmp関数でもそうですが、-1、0、1 のいずれかを返すという仕様ではなく、0より小さい,0,0より大きい値を返すという仕様ですから、減算での実装もあり得ます。
もし、他方の文字列が短い場合には、いずれ、文字列に含まれているある文字と、‘\0’ を比較するときが来ます。これは絶対に一致しませんから、その時点で処理を終了させます。‘\0’ は、数値でいえば 0 なので、‘b’ と ‘x’ のような比較のときと同じ方法で大小関係を行えば、目的どおりの結果を得られます。
非標準の _stricmp関数(→参考)を使うと、目的を達せられます。
#include <stdio.h>
#include <string.h>
/*
文字列を大文字・小文字の違いを無視して比較する
s1: 比較する文字列
s2: 比較する文字列
戻り値: s1 の方が小さい場合は 0 より小さい値、
s1 の方が大きい場合は 0 より大きい値、
s1 と s2 が一致する場合は 0 を返す。
*/
int strcmp_ignorecase(const char* s1, const char* s2)
{
return _stricmp(s1, s2);
}
int main(void)
{
("%d\n", strcmp_ignorecase("abc", "abc"));
printf("%d\n", strcmp_ignorecase("abc", "ABC"));
printf("%d\n", strcmp_ignorecase("abc", "Abc"));
printf("%d\n", strcmp_ignorecase("abc", "ABc"));
printf
("%d\n", strcmp_ignorecase("abc", "axC"));
printf("%d\n", strcmp_ignorecase("axc", "abC"));
printf
("%d\n", strcmp_ignorecase("abcd", "ABc"));
printf("%d\n", strcmp_ignorecase("abc", "ABcd"));
printf("%d\n", strcmp_ignorecase("", "ABc"));
printf("%d\n", strcmp_ignorecase("abc", ""));
printf("%d\n", strcmp_ignorecase("", ""));
printf}
実行結果:
0
0
0
0
-22
22
100
-100
-97
97
0
_stricmp関数を使うには、<string.h> をインクルードします。引数と戻り値の仕様は、strcmp関数と同様です。
_stricmp関数は、内部処理的には、小文字に統一して比較を行います。
return 0;
を削除(C言語編全体でのコードの統一)
Programming Place Plus のトップページへ
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
![]() |
管理者情報 | プライバシーポリシー |