- はじめに 「大文字無視の文字列比較」は“人間の感覚に合わせる”ためのテクニック
- 基本:C#の文字列比較はデフォルトだと「大文字・小文字を区別する」
- よくあるNGパターン:ToLower() / ToUpper() で無理やりそろえる
- 正しい方向性:StringComparison を使って比較ルールを明示する
- 重要ポイント:StringComparison の代表的な種類をざっくり理解する
- 実務で使えるユーティリティメソッドの形
- 部分一致や StartsWith / EndsWith でも大文字無視をしたい場合
- 「どこでも使えるようにする」ための小さな工夫
- まとめ 「大文字無視の文字列比較」は“ルールを明示して、ユーティリティに閉じ込める”
はじめに 「大文字無視の文字列比較」は“人間の感覚に合わせる”ためのテクニック
業務システムでは、ユーザー名、コード値、フラグ文字列などを比較するときに、
「OK と ok は同じとみなしたい」
「Yes と YES と yes を全部同じ扱いにしたい」
という場面がよく出てきます。
人間の感覚では「大文字・小文字が違うだけなら同じでしょ」と思うところを、
プログラムはそのままだと「違う」と判定してしまいます。
そこで必要になるのが、
「大文字・小文字を無視して文字列を比較する」ユーティリティです。
ここでは、C#初心者向けに、
- なぜ単純な
==ではダメなのか - どう書けば“ちゃんとした大文字無視比較”になるのか
- 実務で使いやすいユーティリティの形
を、例題を交えながら丁寧に解説していきます。
基本:C#の文字列比較はデフォルトだと「大文字・小文字を区別する」
== と Equals の素の挙動
まず、C#の文字列比較の基本から確認します。
string a = "Hello";
string b = "hello";
Console.WriteLine(a == b); // False
Console.WriteLine(a.Equals(b)); // False
C#"Hello" と "hello" は、人間から見ると「H が大文字か小文字かの違いだけ」ですが、
C#のデフォルト比較では「別物」として扱われます。
つまり、== や Equals(引数1つのもの)は、大文字・小文字を区別するのが基本です。
「大文字無視で比較したい」ときは、
明示的に“大文字無視”のルールを指定する必要がある、
というのが最初の重要ポイントです。
よくあるNGパターン:ToLower() / ToUpper() で無理やりそろえる
一見正しそうで、実は落とし穴が多い書き方
初心者がよくやるのが、次のような書き方です。
bool IsEqualIgnoreCase_Bad(string? x, string? y)
{
return x?.ToLower() == y?.ToLower();
}
C#一見、「両方小文字にしてから比較しているから、大文字無視になっているじゃん」と思えますが、
実務的にはおすすめできません。
理由はいくつかあります。
nullの扱いが分かりにくい(null?.ToLower()はnullになるが、==の結果をどう解釈するか曖昧になりがち)- 文化(カルチャ)によって大文字・小文字の変換ルールが変わる場合がある
- 文字列を毎回変換するので、パフォーマンス的にも無駄が多い
特に2つ目が重要で、
「大文字・小文字の扱い」は言語や文化によって微妙に違うことがあります。
そのため、単純な ToLower() / ToUpper() では、
思わぬところで比較結果が変わってしまう可能性があります。
正しい方向性:StringComparison を使って比較ルールを明示する
Equals や string.Equals の「第2引数付き」オーバーロード
C#では、文字列比較のルールを指定するために、StringComparison という列挙型が用意されています。
これを使うと、次のように書けます。
bool IsEqualIgnoreCase(string? x, string? y)
{
return string.Equals(x, y, StringComparison.OrdinalIgnoreCase);
}
C#ここで重要なのは、
string.Equals(x, y, ...)を使っていることStringComparison.OrdinalIgnoreCaseを指定していること
の2点です。
StringComparison.OrdinalIgnoreCase は、
「バイトレベルに近い比較(Ordinal)をしつつ、大文字・小文字だけは無視する」
という意味になります。
業務システムで「コード値」「フラグ」「識別子」などを比較するときは、
まずこの OrdinalIgnoreCase を選んでおけば間違いが少ないです。
重要ポイント:StringComparison の代表的な種類をざっくり理解する
Ordinal と CurrentCulture の違い
StringComparison にはいくつか種類がありますが、
実務でよく使うのは主に次の2系統です。
Ordinal/OrdinalIgnoreCaseCurrentCulture/CurrentCultureIgnoreCase
ざっくり言うと、
- Ordinal系
- 文字コード(ほぼバイト列)に近い比較
- 文化(カルチャ)に依存しない
- コード値・ID・キーなどの比較に向いている
- CurrentCulture系
- 実行環境の文化(日本語環境、英語環境など)に応じた比較
- 人間の言語感覚に近い比較
- 画面表示用のソートや、ユーザー入力の比較などに使うことがある
初心者のうちは、
「システム内部の識別子 → OrdinalIgnoreCase」
と覚えておけば十分です。
実務で使えるユーティリティメソッドの形
一番よく使う「大文字無視の等価比較」
まずは、「等しいかどうか」を判定するユーティリティです。
public static class StringCompareUtil
{
public static bool EqualsIgnoreCase(string? x, string? y)
{
return string.Equals(x, y, StringComparison.OrdinalIgnoreCase);
}
}
C#使い方はとてもシンプルです。
Console.WriteLine(StringCompareUtil.EqualsIgnoreCase("OK", "ok")); // True
Console.WriteLine(StringCompareUtil.EqualsIgnoreCase("Yes", "YES")); // True
Console.WriteLine(StringCompareUtil.EqualsIgnoreCase("Test", "TesT")); // True
Console.WriteLine(StringCompareUtil.EqualsIgnoreCase("ABC", "ABD")); // False
Console.WriteLine(StringCompareUtil.EqualsIgnoreCase(null, null)); // True
Console.WriteLine(StringCompareUtil.EqualsIgnoreCase(null, "abc")); // False
C#string.Equals は、両方 null の場合は true を返します。
この挙動をそのまま採用しているので、
「null 同士は同じとみなす」というルールになっています。
もし「null は常に false にしたい」などの要件があれば、
ユーティリティ側で条件分岐を追加すればOKです。
部分一致や StartsWith / EndsWith でも大文字無視をしたい場合
StartsWith / EndsWith にも StringComparison 版がある
「先頭が ABC で始まるかどうかを、大文字無視で判定したい」
といったケースもよくあります。
その場合も、StringComparison を指定できるオーバーロードを使います。
public static class StringCompareUtil
{
public static bool StartsWithIgnoreCase(string? text, string? prefix)
{
if (text is null || prefix is null)
{
return false;
}
return text.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
}
public static bool EndsWithIgnoreCase(string? text, string? suffix)
{
if (text is null || suffix is null)
{
return false;
}
return text.EndsWith(suffix, StringComparison.OrdinalIgnoreCase);
}
}
C#使い方の例はこんな感じです。
Console.WriteLine(StringCompareUtil.StartsWithIgnoreCase("HelloWorld", "hello")); // True
Console.WriteLine(StringCompareUtil.EndsWithIgnoreCase("Report.PDF", ".pdf")); // True
Console.WriteLine(StringCompareUtil.EndsWithIgnoreCase("image.PNG", ".jpg")); // False
C#ファイル拡張子の判定、プレフィックス付きコードの判定など、
実務でかなり頻繁に使うパターンです。
「どこでも使えるようにする」ための小さな工夫
ユーティリティクラスにまとめておくメリット
StringComparison.OrdinalIgnoreCase を毎回書くのは、
正直ちょっと長いです。
そこで、
EqualsIgnoreCaseStartsWithIgnoreCaseEndsWithIgnoreCase
のようなメソッドをユーティリティクラスにまとめておくと、
呼び出し側のコードがかなり読みやすくなります。
if (StringCompareUtil.EqualsIgnoreCase(status, "OK"))
{
// OK扱い
}
if (StringCompareUtil.EndsWithIgnoreCase(fileName, ".csv"))
{
// CSVファイルとして処理
}
C#「何をしたいのか」がメソッド名から一目で分かるので、
後から読む人にも優しいコードになります。
null の扱いを“ユーティリティ側で決めておく”
実務では、「null が来るかもしれない」文字列を比較することが多いです。
毎回呼び出し側で
if (x != null && y != null && x.Equals(...))
C#のように書いていると、コードがすぐにごちゃごちゃします。
ユーティリティ側で、
EqualsIgnoreCase(null, null) → trueEqualsIgnoreCase(null, "abc") → falseStartsWithIgnoreCase(null, "abc") → false
のようなルールを決めておけば、
呼び出し側は「とりあえずそのまま渡せばいい」状態になり、
コードがかなりスッキリします。
まとめ 「大文字無視の文字列比較」は“ルールを明示して、ユーティリティに閉じ込める”
ここまでのポイントを整理すると、こうなります。
C#のデフォルト比較(== や Equals)は、大文字・小文字を区別する。ToLower() / ToUpper() で無理やりそろえる方法は、文化依存やパフォーマンスの面でおすすめできない。string.Equals(x, y, StringComparison.OrdinalIgnoreCase) のように、StringComparison を使って「大文字無視」を明示するのが正攻法。EqualsIgnoreCase / StartsWithIgnoreCase / EndsWithIgnoreCase などをユーティリティ化しておくと、業務コードが読みやすく・安全になる。
「大文字・小文字を無視する」というのは、
一見小さなテクニックですが、
“人間の感覚に近い比較”をプログラムにさせるための大事な一歩です。
ここをきちんと押さえておくと、
認証、設定値判定、ファイル種別判定など、
業務のあちこちで「バグを一つ減らせる」ようになっていきます。
