はじめに 「部分一致検索」は“文字列の中から、手がかりを探す”作業
業務システムでは、こういうことをよくやります。
- 商品名に「りんご」が含まれているデータだけを抽出したい
- ログの1行の中に「ERROR」という文字が含まれているか調べたい
- 入力されたキーワードが、候補リストのどれかに含まれているか調べたい
これが、文字列の「部分一致検索」です。
C#では string.Contains を使うのが基本ですが、
そのままだと「大文字・小文字を区別する」「カルチャの影響を受ける」など、
業務で使うには少し物足りないところがあります。
ここでは、初心者向けに、
- 部分一致検索の基本
- 大文字無視での部分一致
- 実務で使いやすいユーティリティ化
を、例題を交えながら丁寧に解説していきます。
基本:string.Contains で「含まれているか」を調べる
まずは素の Contains の挙動を知る
一番シンプルな部分一致検索は、string.Contains です。
string text = "今日はとてもいい天気です。";
Console.WriteLine(text.Contains("天気")); // True
Console.WriteLine(text.Contains("雨")); // False
C#text.Contains("天気") は、
「text の中に "天気" という文字列が含まれているか?」を調べています。
True → 含まれている
False → 含まれていない
という、とても分かりやすいメソッドです。
ただし、デフォルトでは「大文字・小文字を区別する」
英字を含む文字列で試してみます。
string text = "Error: File Not Found";
Console.WriteLine(text.Contains("Error")); // True
Console.WriteLine(text.Contains("error")); // False
C#"Error" は含まれていますが、"error"(全部小文字)は「含まれていない」と判定されます。
つまり、デフォルトの Contains は大文字・小文字を区別する、ということです。
「ERROR でも error でも ErrOr でもいいから、とにかく“エラー”を検出したい」
という業務的な要件には、そのままだと足りません。
正しい方向性:StringComparison を指定して部分一致する
Contains にも「比較ルールを指定できる版」がある
C# には、比較ルールを指定するための StringComparison という列挙型があります。Equals や StartsWith だけでなく、Contains にもこれを渡せます。
string text = "Error: File Not Found";
bool hasError = text.Contains("error", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(hasError); // True
C#StringComparison.OrdinalIgnoreCase を指定することで、
- 大文字・小文字を無視する
- 文化(カルチャ)に依存しない、シンプルな比較
という動きになります。
業務システムで「コード」「フラグ」「キーワード」などを検索するときは、
まずこの OrdinalIgnoreCase を選んでおけば安全です。
実務で使えるユーティリティメソッドの形
「大文字無視の部分一致」を一言で書けるようにする
毎回 StringComparison.OrdinalIgnoreCase と書くのは長いので、
ユーティリティメソッドに閉じ込めてしまうのがおすすめです。
public static class StringSearchUtil
{
public static bool ContainsIgnoreCase(string? text, string? value)
{
if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(value))
{
return false;
}
return text.Contains(value, StringComparison.OrdinalIgnoreCase);
}
}
C#使い方はこうなります。
Console.WriteLine(StringSearchUtil.ContainsIgnoreCase("Error: File Not Found", "error")); // True
Console.WriteLine(StringSearchUtil.ContainsIgnoreCase("STATUS=OK", "ok")); // True
Console.WriteLine(StringSearchUtil.ContainsIgnoreCase("STATUS=NG", "ok")); // False
Console.WriteLine(StringSearchUtil.ContainsIgnoreCase(null, "ok")); // False
Console.WriteLine(StringSearchUtil.ContainsIgnoreCase("text", null)); // False
C#ここでのポイントは、
nullや空文字が来たら、素直にfalseを返す- 呼び出し側が毎回 null チェックを書かなくていい
という設計にしていることです。
重要ポイント:StringComparison の選び方をざっくり理解する
Ordinal と CurrentCulture の違い
StringComparison にはいくつか種類がありますが、
実務でよく使うのは主に次の2系統です。
Ordinal / OrdinalIgnoreCase
- 文字コード(ほぼバイト列)に近い比較
- 文化(カルチャ)に依存しない
- コード値、ID、フラグ、ログのキーワードなどに向いている
text.Contains("error", StringComparison.OrdinalIgnoreCase);
C#CurrentCulture / CurrentCultureIgnoreCase
- 実行環境の文化(日本語環境、英語環境など)に応じた比較
- 人間の言語感覚に近い比較
- 画面表示用の検索やソートなどで使うことがある
初心者のうちは、
「システム内部の検索 → OrdinalIgnoreCase」
と覚えておけば十分です。
部分一致検索の実務的な使いどころ
ログから「エラー行」だけを拾う
例えば、ログの1行を受け取って、
「エラー行かどうか」を判定するユーティリティを考えてみます。
public static class LogUtil
{
public static bool IsErrorLine(string? line)
{
return StringSearchUtil.ContainsIgnoreCase(line, "error")
|| StringSearchUtil.ContainsIgnoreCase(line, "exception")
|| StringSearchUtil.ContainsIgnoreCase(line, "fatal");
}
}
C#使い方はこうです。
string line1 = "[INFO] Application started.";
string line2 = "[ERROR] Database connection failed.";
string line3 = "[Fatal] Unexpected exception occurred.";
Console.WriteLine(LogUtil.IsErrorLine(line1)); // False
Console.WriteLine(LogUtil.IsErrorLine(line2)); // True
Console.WriteLine(LogUtil.IsErrorLine(line3)); // True
C#ERROR / Error / error など、
大文字・小文字の揺れを気にせずに判定できます。
商品名や説明文からキーワード検索
public static bool ContainsKeyword(string? text, string? keyword)
{
return StringSearchUtil.ContainsIgnoreCase(text, keyword);
}
C#string name = "青森県産りんごジュース";
Console.WriteLine(ContainsKeyword(name, "りんご")); // True
Console.WriteLine(ContainsKeyword(name, "みかん")); // False
C#日本語の場合は大文字・小文字の問題は少ないですが、
英数字が混ざるケース(型番、コードなど)では IgnoreCase が効いてきます。
もう一歩:インデックスが欲しい場合は IndexOf を使う
「どこに含まれているか」まで知りたいとき
Contains は「含まれているかどうか」だけですが、
「何文字目に出てきたか」を知りたいときは IndexOf を使います。
public static int IndexOfIgnoreCase(string? text, string? value)
{
if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(value))
{
return -1;
}
return text.IndexOf(value, StringComparison.OrdinalIgnoreCase);
}
C#使い方はこうです。
string text = "Error: File Not Found";
int index = IndexOfIgnoreCase(text, "file");
Console.WriteLine(index); // 7(0始まり)
C#-1 → 見つからない
0以上 → 見つかった位置
というルールです。
「見つかったら、その前後の文字列を切り出したい」
「最初に出てきた位置だけ知りたい」
といったときに便利です。
業務ユーティリティとしてどうまとめるか
「検索系」をひとまとめにしておく
実務でよく使うのは、だいたい次の3つです。
public static class StringSearchUtil
{
public static bool ContainsIgnoreCase(string? text, string? value)
{
if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(value))
{
return false;
}
return text.Contains(value, StringComparison.OrdinalIgnoreCase);
}
public static int IndexOfIgnoreCase(string? text, string? value)
{
if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(value))
{
return -1;
}
return text.IndexOf(value, StringComparison.OrdinalIgnoreCase);
}
public static bool StartsWithIgnoreCase(string? text, string? prefix)
{
if (text is null || prefix is null)
{
return false;
}
return text.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
}
}
C#これを用意しておくと、呼び出し側は
if (StringSearchUtil.ContainsIgnoreCase(message, "timeout"))
{
// タイムアウト系エラーとして扱う
}
if (StringSearchUtil.StartsWithIgnoreCase(code, "ERR"))
{
// エラーコードとして扱う
}
C#のように、「何をしたいか」が一目で分かるコードを書けます。
null の扱いを“ユーティリティ側で固定する”
今回の実装では、
textかvalueが null / 空 → 「見つからない」と判定(false/-1)
というルールにしています。
これにより、呼び出し側は
- 毎回 null チェックを書かなくていい
- 「とりあえずそのまま渡せばいい」という気持ちで使える
ようになります。
プロジェクトによっては、
- null が来たら例外
- 空文字は特別扱い
などのポリシーもあり得ますが、
大事なのは「ユーティリティ側でルールを決めておく」ことです。
まとめ 「部分一致検索ユーティリティ」は“文字列の中から意味をすくい上げる道具”
部分一致検索は、ログ解析、検索機能、バリデーション、コード判定など、
業務のあちこちで使われる“地味だけど超重要なテクニック”です。
押さえておきたいポイントは、
string.Containsはデフォルトだと大文字・小文字を区別すること。Contains(value, StringComparison.OrdinalIgnoreCase)のように、比較ルールを明示するのが実務的で安全なこと。ContainsIgnoreCaseやIndexOfIgnoreCaseをユーティリティ化しておくと、呼び出し側のコードが読みやすく・バグりにくくなること。- null や空文字の扱いをユーティリティ側で決めておくと、業務コードがすっきりすること。
ここまで理解できれば、「なんとなく Contains している」段階から一歩進んで、
“意図のはっきりした、業務で使える部分一致検索ユーティリティ”を、
自分のC#コードの中に自然に組み込めるようになっていきます。
