C# Tips | 文字列処理:文字列比較(大文字無視)

C# C#
スポンサーリンク

はじめに 「大文字無視の文字列比較」は“人間の感覚に合わせる”ためのテクニック

業務システムでは、ユーザー名、コード値、フラグ文字列などを比較するときに、
OKok は同じとみなしたい」
YesYESyes を全部同じ扱いにしたい」
という場面がよく出てきます。

人間の感覚では「大文字・小文字が違うだけなら同じでしょ」と思うところを、
プログラムはそのままだと「違う」と判定してしまいます。

そこで必要になるのが、
「大文字・小文字を無視して文字列を比較する」ユーティリティです。

ここでは、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#

一見、「両方小文字にしてから比較しているから、大文字無視になっているじゃん」と思えますが、
実務的にはおすすめできません。

理由はいくつかあります。

  1. null の扱いが分かりにくい(null?.ToLower()null になるが、== の結果をどう解釈するか曖昧になりがち)
  2. 文化(カルチャ)によって大文字・小文字の変換ルールが変わる場合がある
  3. 文字列を毎回変換するので、パフォーマンス的にも無駄が多い

特に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 / OrdinalIgnoreCase
  • CurrentCulture / 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 を毎回書くのは、
正直ちょっと長いです。

そこで、

  • EqualsIgnoreCase
  • StartsWithIgnoreCase
  • EndsWithIgnoreCase

のようなメソッドをユーティリティクラスにまとめておくと、
呼び出し側のコードがかなり読みやすくなります。

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) → true
  • EqualsIgnoreCase(null, "abc") → false
  • StartsWithIgnoreCase(null, "abc") → false

のようなルールを決めておけば、
呼び出し側は「とりあえずそのまま渡せばいい」状態になり、
コードがかなりスッキリします。


まとめ 「大文字無視の文字列比較」は“ルールを明示して、ユーティリティに閉じ込める”

ここまでのポイントを整理すると、こうなります。

C#のデフォルト比較(==Equals)は、大文字・小文字を区別する。
ToLower() / ToUpper() で無理やりそろえる方法は、文化依存やパフォーマンスの面でおすすめできない。
string.Equals(x, y, StringComparison.OrdinalIgnoreCase) のように、StringComparison を使って「大文字無視」を明示するのが正攻法。
EqualsIgnoreCase / StartsWithIgnoreCase / EndsWithIgnoreCase などをユーティリティ化しておくと、業務コードが読みやすく・安全になる。

「大文字・小文字を無視する」というのは、
一見小さなテクニックですが、
“人間の感覚に近い比較”をプログラムにさせるための大事な一歩です。

ここをきちんと押さえておくと、
認証、設定値判定、ファイル種別判定など、
業務のあちこちで「バグを一つ減らせる」ようになっていきます。

タイトルとURLをコピーしました