C#で文字列検索する方法まとめ|IndexOf・Contains・正規表現の使い分けと高速化のコツ
はじめに
C#で文字列検索を行う場面は非常に多くあります。たとえば、ユーザー入力に特定のキーワードが含まれているかを調べる、ファイル名の拡張子を判定する、ログの中からエラー行を探す、メールアドレスや日付のようなパターンを抽出する、といった処理です。
C#には文字列検索のために、Contains、IndexOf、StartsWith、EndsWith、Regexなど複数の方法が用意されています。それぞれ得意な用途が異なるため、目的に合った方法を選ぶことが重要です。
単に「含まれているか」を調べたいだけならContains、位置も必要ならIndexOf、先頭や末尾を判定するならStartsWithやEndsWith、複雑な条件で検索するなら正規表現を使うのが基本です。また、C#の文字列検索では大文字小文字、カルチャ、日本語、全角半角なども考慮する必要があります。
この記事では、C#で文字列検索を行う代表的な方法を、実践コードとあわせてわかりやすく解説します。
1. C#の文字列検索でできることと基本方針
C#の文字列検索では、単純なキーワード検索から複雑なパターン検索まで幅広い処理ができます。まずは、どのような検索があるのかを整理しておきましょう。
1-1. 文字列検索とは何か
文字列検索とは、ある文字列の中に、指定した文字や単語、パターンが存在するかを調べる処理です。
たとえば、次のような処理が文字列検索にあたります。
C#string text = "C#で文字列検索する方法";
bool result = text.Contains("検索");
Console.WriteLine(result); // True
この例では、textの中に"検索"という文字列が含まれているかを調べています。
C#では、文字列を表すstring型に多くの検索用メソッドが用意されています。Containsは指定した文字列が含まれるかをboolで返し、IndexOfは指定した文字列が最初に現れる位置を返します。Microsoftの公式ドキュメントでも、Contains(String, StringComparison)は指定した比較ルールで文字列が含まれるかを返すメソッド、IndexOfは最初に見つかった位置を返すメソッドとして説明されています。Microsoft Learn+1
1-2. 完全一致・部分一致・前方一致・後方一致・パターン一致の違い
文字列検索では、検索方法の種類を理解しておくことが大切です。
完全一致は、文字列全体が一致するかを調べる方法です。
C#string input = "admin";
bool result = input == "admin";
部分一致は、文字列の一部に指定したキーワードが含まれるかを調べる方法です。
C#string text = "C#で文字列検索する";
bool result = text.Contains("文字列");
前方一致は、文字列が指定した文字列で始まるかを調べる方法です。
C#string fileName = "report_2026.csv";
bool result = fileName.StartsWith("report");
後方一致は、文字列が指定した文字列で終わるかを調べる方法です。
C#string fileName = "data.csv";
bool result = fileName.EndsWith(".csv");
パターン一致は、正規表現を使って「数字が含まれる」「メールアドレス形式である」「日付形式に一致する」など、柔軟な条件で検索する方法です。
C#using System.Text.RegularExpressions;
string input = "注文番号: 12345";
bool result = Regex.IsMatch(input, @"\d+");
単純な検索であればContainsやIndexOfを使い、複雑な条件が必要な場合だけ正規表現を使うのが基本です。
1-3. IndexOf・Contains・正規表現の使い分け早見表
C#で文字列検索を行うときは、目的に応じて次のように使い分けます。
| やりたいこと | 使うメソッド | 戻り値 |
|---|---|---|
| 含まれるかだけ知りたい | Contains | bool |
| 見つかった位置を知りたい | IndexOf | int |
| 後ろから探したい | LastIndexOf | int |
| 先頭で始まるか調べたい | StartsWith | bool |
| 末尾で終わるか調べたい | EndsWith | bool |
| 複雑なパターンで探したい | Regex.IsMatch / Regex.Match / Regex.Matches | bool / Match / MatchCollection |
| 配列やListから探したい | LINQ、Find、Any | 要件による |
基本的には、単純な文字列検索ではContainsやIndexOfを使い、正規表現は必要なときだけ使います。
1-4. まず押さえるべきStringComparisonの重要性
C#の文字列検索では、StringComparisonを指定することが非常に重要です。
たとえば、次のコードでは大文字小文字が区別されます。
C#string text = "Hello C#";
Console.WriteLine(text.Contains("hello")); // False
大文字小文字を区別せずに検索したい場合は、StringComparison.OrdinalIgnoreCaseを指定します。
C#string text = "Hello C#";
bool result = text.Contains("hello", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(result); // True
StringComparisonには、Ordinal、OrdinalIgnoreCase、CurrentCulture、CurrentCultureIgnoreCaseなどがあります。Microsoftの文字列比較のベストプラクティスでは、文字列比較メソッドを呼び出すときは比較方法を明示するオーバーロードを使うことが推奨されています。Microsoft Learn+1
特に、ID、キー、ファイルパス、URL、設定値、ログなど、言語的な意味よりも機械的な一致を重視する文字列では、StringComparison.OrdinalまたはStringComparison.OrdinalIgnoreCaseを使うのが基本です。
2. Containsで文字列を含むか判定する方法
Containsは、C#で文字列検索を行うときに最もよく使うメソッドのひとつです。指定した文字列が含まれているかどうかをtrueまたはfalseで返します。
2-1. Containsの基本的な使い方
Containsの基本構文は次のとおりです。
C#文字列.Contains(検索文字列)
実際のコード例を見てみましょう。
C#string text = "C#で文字列検索を学ぶ";
bool hasKeyword = text.Contains("検索");
Console.WriteLine(hasKeyword); // True
"検索"が含まれているため、結果はtrueになります。
含まれていない場合はfalseです。
C#string text = "C#で文字列検索を学ぶ";
bool hasKeyword = text.Contains("Java");
Console.WriteLine(hasKeyword); // False
Containsは、検索対象の有無だけを知りたいときに最適です。見つかった位置が必要ない場合は、IndexOfよりも読みやすいコードになります。
2-2. 大文字小文字を区別せずに検索する方法
大文字小文字を無視して検索するには、StringComparison.OrdinalIgnoreCaseを指定します。
C#string text = "CSharp Programming";
bool result = text.Contains("csharp", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(result); // True
StringComparison.OrdinalIgnoreCaseを使うことで、"CSharp"と"csharp"を同じものとして扱えます。
古い環境などでContainsにStringComparisonを指定できない場合は、IndexOfを使って同じように判定できます。
C#string text = "CSharp Programming";
bool result = text.IndexOf("csharp", StringComparison.OrdinalIgnoreCase) >= 0;
Console.WriteLine(result); // True
IndexOfは見つからない場合に-1を返すため、>= 0で判定します。
2-3. StringComparisonを使ったComparisonの重要性
Containsを使うときは、可能な限りStringComparisonを指定しましょう。
C#string text = "example@example.com";
bool result = text.Contains("@example.com", StringComparison.OrdinalIgnoreCase);
このように書くと、比較ルールが明確になります。
StringComparisonを指定しないコードは短く書けますが、大文字小文字やカルチャの扱いが読み手に伝わりにくくなります。特に、業務システムやWebアプリケーションでは、検索条件が明確であることが重要です。
機械的な文字列検索では、次のような指定をよく使います。
C#StringComparison.Ordinal
StringComparison.OrdinalIgnoreCase
ユーザーに表示される自然言語の並び替えや検索では、カルチャを考慮する場合があります。
C#StringComparison.CurrentCulture
StringComparison.CurrentCultureIgnoreCase
ただし、プログラム内部のキーやコード値、URL、ファイル名、識別子などでは、カルチャに依存しないOrdinal系を選ぶほうが安全です。
2-安全なContains
実務では、検索対象や検索キーワードがnullになる可能性があります。そのままContainsを呼び出すと、NullReferenceExceptionが発生することがあります。
安全にContainsを使うには、次のようなヘルパーメソッドを用意すると便利です。
C#public static bool SafeContains(string? source, string? keyword)
{
if (string.IsNullOrEmpty(source))
{
return false;
}
if (string.IsNullOrEmpty(keyword))
{
return false;
}
return source.Contains(keyword, StringComparison.OrdinalIgnoreCase);
}
使用例は次のとおりです。
C#string? text = null;
bool result = SafeContains(text, "error");
Console.WriteLine(result); // False
sourceがnullでも例外が発生せず、falseを返します。
空文字を検索キーワードとして許可するかどうかは、アプリケーションの仕様によって決める必要があります。多くの場合、検索キーワードが空なら検索しない扱いにするほうが自然です。
2-4. Containsで検索できないケースと注意点
Containsは便利ですが、すべての検索に向いているわけではありません。
たとえば、次のような検索には不向きです。
C#// 数字が含まれるか調べたい
// メールアドレス形式か判定したい
// yyyy/MM/dd形式の日付を抽出したい
// 複数の空白や記号の揺れを許容したい
このような場合は、正規表現を使うほうが適しています。
また、Containsは「どこに含まれているか」を返しません。位置が必要な場合はIndexOfを使います。
C#string text = "ABC-123-XYZ";
int index = text.IndexOf("123", StringComparison.Ordinal);
Console.WriteLine(index); // 4
さらに、Containsは単純な部分一致なので、単語単位の検索には注意が必要です。
C#string text = "cat catalog category";
bool result = text.Contains("cat");
Console.WriteLine(result); // True
この場合、catだけでなくcatalogやcategoryにも一致します。単語単位で検索したい場合は、正規表現で単語境界を使うことを検討します。
C#using System.Text.RegularExpressions;
string text = "cat catalog category";
bool result = Regex.IsMatch(text, @"\bcat\b");
Console.WriteLine(result); // True
2-5. nullや空文字を扱うときのエラー対策
Containsを呼び出す対象がnullの場合、例外が発生します。
C#string? text = null;
// NullReferenceException
bool result = text.Contains("test");
これを防ぐには、事前にnullチェックを行います。
C#string? text = null;
bool result = text != null && text.Contains("test", StringComparison.Ordinal);
Console.WriteLine(result); // False
null条件演算子を使う方法もあります。
C#string? text = null;
bool result = text?.Contains("test", StringComparison.Ordinal) == true;
Console.WriteLine(result); // False
検索キーワードがnullになる可能性がある場合もチェックしましょう。
C#string text = "C# Sample";
string? keyword = null;
bool result = !string.IsNullOrEmpty(keyword)
&& text.Contains(keyword, StringComparison.OrdinalIgnoreCase);
Console.WriteLine(result); // False
検索処理では、検索対象と検索キーワードの両方に対してnullや空文字の扱いを決めておくことが重要です。
3. IndexOfで文字列の位置を検索する方法
IndexOfは、指定した文字列が最初に見つかった位置を返すメソッドです。Containsがtrueまたはfalseを返すのに対して、IndexOfはインデックス番号を返します。
3-1. IndexOfの基本構文と戻り値
IndexOfの基本構文は次のとおりです。
C#文字列.IndexOf(検索文字列)
コード例を見てみましょう。
C#string text = "C# string search";
int index = text.IndexOf("string", StringComparison.Ordinal);
Console.WriteLine(index); // 3
C#の文字列インデックスは0から始まります。
C # s t r i n g s e a r c h
0 1 2 3 4 5 6 7 8 ...
そのため、"string"は3番目の位置から始まるため、戻り値は3になります。
3-2. 見つからない場合の-1判定
IndexOfは、検索文字列が見つからない場合に-1を返します。
C#string text = "C# string search";
int index = text.IndexOf("Java", StringComparison.Ordinal);
Console.WriteLine(index); // -1
そのため、存在チェックをする場合は次のように判定します。
C#string text = "C# string search";
if (text.IndexOf("string", StringComparison.Ordinal) >= 0)
{
Console.WriteLine("見つかりました");
}
else
{
Console.WriteLine("見つかりませんでした");
}
> 0で判定してしまうと、先頭に見つかった場合を見落とすため注意してください。
C#string text = "C# sample";
int index = text.IndexOf("C#", StringComparison.Ordinal);
Console.WriteLine(index); // 0
先頭に一致した場合は0が返ります。存在判定では必ず>= 0または!= -1を使いましょう。
3-3. 検索開始位置を指定する方法
IndexOfでは、検索を開始する位置を指定できます。
C#string text = "abc abc abc";
int first = text.IndexOf("abc", StringComparison.Ordinal);
int second = text.IndexOf("abc", first + 1, StringComparison.Ordinal);
Console.WriteLine(first); // 0
Console.WriteLine(second); // 4
最初のabcは位置0、次のabcは位置4にあります。
すべての一致位置を探したい場合は、ループを使います。
C#string text = "abc abc abc";
string keyword = "abc";
int index = 0;
while ((index = text.IndexOf(keyword, index, StringComparison.Ordinal)) >= 0)
{
Console.WriteLine($"見つかった位置: {index}");
index += keyword.Length;
}
出力結果は次のようになります。
見つかった位置: 0
見つかった位置: 4
見つかった位置: 8
同じキーワードを繰り返し検索する場合、IndexOfの開始位置指定は非常に便利です。
3-4. 大文字小文字を無視してIndexOfする方法
IndexOfでもStringComparison.OrdinalIgnoreCaseを指定すれば、大文字小文字を無視できます。
C#string text = "Error: File Not Found";
int index = text.IndexOf("file", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(index); // 7
大文字小文字を無視して存在判定したい場合は、次のように書けます。
C#string text = "Error: File Not Found";
bool exists = text.IndexOf("file", StringComparison.OrdinalIgnoreCase) >= 0;
Console.WriteLine(exists); // True
ContainsにStringComparisonを指定できる環境であれば、存在判定だけならContainsのほうが読みやすくなります。
C#bool exists = text.Contains("file", StringComparison.OrdinalIgnoreCase);
位置が必要な場合はIndexOfを使いましょう。
3-5. LastIndexOfで後ろから検索する方法
LastIndexOfを使うと、文字列の後ろから検索できます。
C#string path = @"C:\work\logs\app.log";
int index = path.LastIndexOf(@"\", StringComparison.Ordinal);
Console.WriteLine(index);
ファイルパスの最後の区切り位置を探すようなケースで便利です。
ファイル名だけを取り出す例です。
C#string path = @"C:\work\logs\app.log";
int index = path.LastIndexOf(@"\", StringComparison.Ordinal);
string fileName = index >= 0 ? path.Substring(index + 1) : path;
Console.WriteLine(fileName); // app.log
ただし、ファイルパスを扱う場合は、通常はSystem.IO.Pathクラスを使うほうが安全です。
C#using System.IO;
string path = @"C:\work\logs\app.log";
string fileName = Path.GetFileName(path);
Console.WriteLine(fileName); // app.log
文字列として最後の出現位置を知りたい場合はLastIndexOf、パス操作そのものをしたい場合はPathクラスを使い分けましょう。
3-6. ContainsではなくIndexOfを使うべきケース
ContainsではなくIndexOfを使うべきなのは、見つかった位置が必要なケースです。
たとえば、特定の文字列より後ろを取り出したい場合です。
C#string text = "Name: Taro";
int index = text.IndexOf(":", StringComparison.Ordinal);
if (index >= 0)
{
string value = text.Substring(index + 1).Trim();
Console.WriteLine(value); // Taro
}
また、複数の一致箇所を順番に処理したい場合もIndexOfが向いています。
C#string text = "error warning error info error";
string keyword = "error";
int index = 0;
int count = 0;
while ((index = text.IndexOf(keyword, index, StringComparison.OrdinalIgnoreCase)) >= 0)
{
count++;
index += keyword.Length;
}
Console.WriteLine(count); // 3
存在判定だけならContains、位置が必要ならIndexOfと覚えておくと使い分けやすくなります。
4. StartsWith・EndsWithで前方一致・後方一致検索する方法
StartsWithとEndsWithは、文字列の先頭または末尾が指定した文字列と一致するかを判定するメソッドです。
4-1. StartsWithで先頭文字列を判定する
StartsWithは、文字列が指定した文字列で始まるかを調べます。
C#string text = "https://example.com";
bool result = text.StartsWith("https://", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(result); // True
URL、接頭辞、コード体系、ログレベルなどの判定によく使います。
C#string log = "ERROR: File not found";
if (log.StartsWith("ERROR", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("エラーログです");
}
Containsでも"ERROR"が含まれるかは調べられますが、先頭にあることを条件にしたい場合はStartsWithを使うほうが明確です。
4-2. EndsWithで末尾文字列を判定する
EndsWithは、文字列が指定した文字列で終わるかを調べます。
C#string fileName = "report.csv";
bool result = fileName.EndsWith(".csv", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(result); // True
ファイル拡張子、URL末尾、接尾辞などの判定に使えます。
C#string imageName = "photo.JPG";
if (imageName.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("JPEG画像です");
}
拡張子判定では、大文字小文字が混在する可能性があるため、OrdinalIgnoreCaseを指定すると実用的です。
4-3. 拡張子・接頭辞・URL判定での活用例
CSVファイルかどうかを判定する例です。
C#string fileName = "sales_2026.CSV";
bool isCsv = fileName.EndsWith(".csv", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(isCsv); // True
商品コードの接頭辞を判定する例です。
C#string productCode = "PRD-00123";
bool isProduct = productCode.StartsWith("PRD-", StringComparison.Ordinal);
Console.WriteLine(isProduct); // True
URLがHTTPSかどうかを判定する例です。
C#string url = "https://example.com/page";
bool isHttps = url.StartsWith("https://", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(isHttps); // True
ただし、URLを厳密に扱う場合は、単純な文字列判定ではなくUriクラスを使うほうが安全です。
C#Uri uri = new Uri("https://example.com/page");
bool isHttps = uri.Scheme == Uri.UriSchemeHttps;
Console.WriteLine(isHttps); // True
文字列として簡単に判定したい場合はStartsWithやEndsWith、構造を正確に扱いたい場合は専用クラスを使うのがよいです。
4-4. StringComparisonを指定する実装例
StartsWithやEndsWithでも、StringComparisonを指定できます。
C#string fileName = "README.MD";
bool isMarkdown = fileName.EndsWith(".md", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(isMarkdown); // True
C#string command = "START server";
bool isStartCommand = command.StartsWith("start", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(isStartCommand); // True
比較ルールを省略せずに書くことで、大文字小文字やカルチャの扱いが明確になります。
C#public static bool HasPrefix(string text, string prefix)
{
return text.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
}
public static bool HasSuffix(string text, string suffix)
{
return text.EndsWith(suffix, StringComparison.OrdinalIgnoreCase);
}
単純な前方一致・後方一致では、RegexよりもStartsWithやEndsWithを使うほうが読みやすく、意図も明確です。
5. 正規表現で複雑な文字列検索をする方法
正規表現を使うと、数字、英字、メールアドレス、日付、電話番号のようなパターンを検索できます。C#ではSystem.Text.RegularExpressions.Regexクラスを使います。
5-1. Regex.IsMatchの基本的な使い方
Regex.IsMatchは、入力文字列が指定した正規表現パターンに一致するかをboolで返します。Microsoftの公式ドキュメントでも、Regex.IsMatchは指定した正規表現が入力文字列内で一致を検出するかどうかを示すメソッドとして説明されています。Microsoft Learn+1
C#using System.Text.RegularExpressions;
string input = "abc123";
bool hasNumber = Regex.IsMatch(input, @"\d");
Console.WriteLine(hasNumber); // True
\dは数字を表す正規表現です。inputに数字が含まれているため、結果はtrueになります。
文字列全体が数字だけかを判定する場合は、次のように書きます。
C#using System.Text.RegularExpressions;
string input = "12345";
bool isNumber = Regex.IsMatch(input, @"^\d+$");
Console.WriteLine(isNumber); // True
^は文字列の先頭、$は文字列の末尾、\d+は1文字以上の数字を意味します。
5-2. Regex.Matchで一致した文字列を取得する
Regex.Matchを使うと、一致した文字列を取得できます。
C#using System.Text.RegularExpressions;
string text = "注文番号は ABC-12345 です。";
Match match = Regex.Match(text, @"[A-Z]+-\d+");
if (match.Success)
{
Console.WriteLine(match.Value); // ABC-12345
}
[A-Z]+は1文字以上の英大文字、\d+は1文字以上の数字を表します。
IsMatchは一致するかどうかだけを知りたいときに使い、Matchは一致した内容を取り出したいときに使います。
5-3. Regex.Matchesで複数箇所を検索する
複数の一致箇所をすべて取得したい場合は、Regex.Matchesを使います。
C#using System.Text.RegularExpressions;
string text = "商品A: 120円, 商品B: 300円, 商品C: 450円";
MatchCollection matches = Regex.Matches(text, @"\d+");
foreach (Match match in matches)
{
Console.WriteLine(match.Value);
}
出力結果は次のとおりです。
120
300
450
ログや文章の中から複数のID、金額、日付などを抽出したい場合に便利です。
5-4. 数字・英字・メールアドレス・日付を検索する例
数字を検索する例です。
C#using System.Text.RegularExpressions;
string text = "abc123";
bool hasNumber = Regex.IsMatch(text, @"\d");
Console.WriteLine(hasNumber); // True
英字を検索する例です。
C#using System.Text.RegularExpressions;
string text = "商品ABC";
bool hasAlphabet = Regex.IsMatch(text, @"[A-Za-z]");
Console.WriteLine(hasAlphabet); // True
メールアドレスらしき文字列を検索する例です。
C#using System.Text.RegularExpressions;
string text = "お問い合わせは support@example.com まで";
Match match = Regex.Match(text, @"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}");
if (match.Success)
{
Console.WriteLine(match.Value); // support@example.com
}
日付を検索する例です。
C#using System.Text.RegularExpressions;
string text = "期限は2026/06/18です。";
Match match = Regex.Match(text, @"\d{4}/\d{2}/\d{2}");
if (match.Success)
{
Console.WriteLine(match.Value); // 2026/06/18
}
ただし、メールアドレスや日付を厳密に検証するには、正規表現だけでは不十分な場合があります。日付であればDateTime.TryParseExact、メールアドレスであれば用途に応じた専用の検証処理も検討しましょう。
5-5. RegexOptions.IgnoreCaseで大文字小文字を無視する
正規表現で大文字小文字を無視したい場合は、RegexOptions.IgnoreCaseを指定します。
C#using System.Text.RegularExpressions;
string text = "CSharp Programming";
bool result = Regex.IsMatch(text, "csharp", RegexOptions.IgnoreCase);
Console.WriteLine(result); // True
複数のオプションを指定することもできます。
C#using System.Text.RegularExpressions;
string text = "ERROR: File not found";
bool result = Regex.IsMatch(
text,
@"^error",
RegexOptions.IgnoreCase
);
Console.WriteLine(result); // True
正規表現でも、大文字小文字を区別するかどうかは明示的に指定するのがおすすめです。
5-6. 正規表現を使うべきケース・避けるべきケース
正規表現を使うべきなのは、次のようなケースです。
数字だけを抽出したい
メールアドレス形式を探したい
複数の空白を許容したい
日付形式を検索したい
英字と数字の組み合わせを判定したい
複雑なログ形式から値を取り出したい
一方で、次のような単純検索では正規表現を使わないほうがよいです。
特定のキーワードが含まれるか
先頭が特定の文字列か
末尾が特定の拡張子か
完全一致するか
単純な検索であれば、Contains、IndexOf、StartsWith、EndsWithのほうが読みやすく、保守しやすいです。
C#// 単純検索ならこちらで十分
bool result = text.Contains("error", StringComparison.OrdinalIgnoreCase);
正規表現は強力ですが、読みづらくなりやすいため、必要な場面に絞って使いましょう。
6. 文字列検索でよく使う実践コード例
ここからは、C#の文字列検索でよく使う実践的なコード例を紹介します。
6-1. 文字列に特定キーワードが含まれるか調べる
特定キーワードが含まれるかを調べる基本例です。
C#string text = "C#で文字列検索を学びます。";
string keyword = "検索";
bool result = text.Contains(keyword, StringComparison.Ordinal);
Console.WriteLine(result); // True
大文字小文字を無視したい場合は、OrdinalIgnoreCaseを使います。
C#string text = "Error: File Not Found";
string keyword = "error";
bool result = text.Contains(keyword, StringComparison.OrdinalIgnoreCase);
Console.WriteLine(result); // True
ユーザー入力を検索する場合は、nullや空文字も考慮しましょう。
C#public static bool ContainsKeyword(string? text, string? keyword)
{
return !string.IsNullOrEmpty(text)
&& !string.IsNullOrEmpty(keyword)
&& text.Contains(keyword, StringComparison.OrdinalIgnoreCase);
}
6-2. 複数キーワードのいずれかを含むか判定する
複数のキーワードのうち、どれか1つでも含まれているかを調べるには、LINQのAnyを使うと簡潔です。
C#using System.Linq;
string text = "エラーが発生しました。";
string[] keywords = { "エラー", "警告", "失敗" };
bool result = keywords.Any(keyword =>
text.Contains(keyword, StringComparison.OrdinalIgnoreCase));
Console.WriteLine(result); // True
Anyは、条件を満たす要素が1つでもあればtrueを返します。
キーワードリストが空の場合はfalseになります。
C#string[] keywords = Array.Empty<string>();
bool result = keywords.Any(keyword =>
text.Contains(keyword, StringComparison.OrdinalIgnoreCase));
Console.WriteLine(result); // False
検索キーワードにnullや空文字が含まれる可能性がある場合は、除外してから検索すると安全です。
C#string text = "エラーが発生しました。";
string?[] keywords = { null, "", "エラー" };
bool result = keywords
.Where(keyword => !string.IsNullOrEmpty(keyword))
.Any(keyword => text.Contains(keyword!, StringComparison.OrdinalIgnoreCase));
Console.WriteLine(result); // True
6-3. 複数キーワードをすべて含むか判定する
複数のキーワードをすべて含んでいるかを調べるには、LINQのAllを使います。
C#using System.Linq;
string text = "C#で文字列検索と正規表現を学ぶ";
string[] keywords = { "C#", "検索", "正規表現" };
bool result = keywords.All(keyword =>
text.Contains(keyword, StringComparison.OrdinalIgnoreCase));
Console.WriteLine(result); // True
Allは、すべての要素が条件を満たす場合にtrueを返します。
検索フォームでAND検索を実装する場合にも使えます。
C#public static bool ContainsAllKeywords(string text, IEnumerable<string> keywords)
{
return keywords
.Where(keyword => !string.IsNullOrWhiteSpace(keyword))
.All(keyword => text.Contains(keyword, StringComparison.OrdinalIgnoreCase));
}
使用例です。
C#string text = "C# IndexOf Contains Regex";
string[] keywords = { "C#", "Regex" };
bool result = ContainsAllKeywords(text, keywords);
Console.WriteLine(result); // True
6-4. 文字列の中から一致箇所を抽出する
一致箇所を抽出したい場合は、Regex.MatchやRegex.Matchesを使います。
最初の一致だけを取得する例です。
C#using System.Text.RegularExpressions;
string text = "注文番号: ORD-2026-001";
Match match = Regex.Match(text, @"ORD-\d{4}-\d{3}");
if (match.Success)
{
Console.WriteLine(match.Value); // ORD-2026-001
}
複数の一致をすべて取得する例です。
C#using System.Text.RegularExpressions;
string text = "ID: 101, ID: 202, ID: 303";
MatchCollection matches = Regex.Matches(text, @"\d+");
foreach (Match match in matches)
{
Console.WriteLine(match.Value);
}
出力結果は次のとおりです。
101
202
303
単純な区切り文字で抽出できる場合は、正規表現ではなくSplitのほうが簡単です。
C#string csv = "apple,banana,orange";
string[] items = csv.Split(',');
foreach (string item in items)
{
Console.WriteLine(item);
}
処理内容に応じて、Regex、IndexOf、Splitを使い分けましょう。
6-5. 配列やListの中から文字列を検索する
配列の中から完全一致する文字列を探す場合は、Array.ExistsやLINQを使えます。
C#string[] names = { "Taro", "Hanako", "Jiro" };
bool exists = names.Contains("Taro");
Console.WriteLine(exists); // True
大文字小文字を無視したい場合は、LINQのAnyを使います。
C#using System.Linq;
string[] names = { "Taro", "Hanako", "Jiro" };
bool exists = names.Any(name =>
string.Equals(name, "taro", StringComparison.OrdinalIgnoreCase));
Console.WriteLine(exists); // True
List<string>から検索する例です。
C#using System.Collections.Generic;
using System.Linq;
List<string> files = new List<string>
{
"report.csv",
"image.png",
"readme.md"
};
bool hasCsv = files.Any(file =>
file.EndsWith(".csv", StringComparison.OrdinalIgnoreCase));
Console.WriteLine(hasCsv); // True
部分一致で検索する場合は、各要素に対してContainsを使います。
C#List<string> logs = new List<string>
{
"INFO: Started",
"ERROR: File not found",
"WARN: Low memory"
};
List<string> errors = logs
.Where(log => log.Contains("ERROR", StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (string error in errors)
{
Console.WriteLine(error);
}
6-6. LINQを使って条件に一致する文字列を探す
LINQを使うと、配列やリストの中から条件に一致する文字列を簡潔に検索できます。
最初に一致する要素を取得する例です。
C#using System.Linq;
List<string> users = new List<string>
{
"admin@example.com",
"user@example.com",
"guest@test.com"
};
string? user = users.FirstOrDefault(x =>
x.Contains("example.com", StringComparison.OrdinalIgnoreCase));
Console.WriteLine(user); // admin@example.com
条件に一致するすべての要素を取得する例です。
C#using System.Linq;
List<string> users = new List<string>
{
"admin@example.com",
"user@example.com",
"guest@test.com"
};
List<string> exampleUsers = users
.Where(x => x.EndsWith("@example.com", StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (string user in exampleUsers)
{
Console.WriteLine(user);
}
複数条件で検索することもできます。
C#List<string> files = new List<string>
{
"2026_report.csv",
"2025_report.csv",
"memo.txt",
"2026_sales.csv"
};
List<string> results = files
.Where(file =>
file.StartsWith("2026", StringComparison.OrdinalIgnoreCase)
&& file.EndsWith(".csv", StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (string file in results)
{
Console.WriteLine(file);
}
LINQは便利ですが、大量データを処理する場合は、不要なToListを避けるなど、パフォーマンスにも注意しましょう。
7. 大文字小文字・全角半角・カルチャを考慮した検索
C#の文字列検索では、単にキーワードが含まれるかだけでなく、大文字小文字、カルチャ、日本語特有の表記揺れも考慮する必要があります。
7-1. OrdinalとCurrentCultureの違い
StringComparison.Ordinalは、文字列をUnicodeのコード値に基づいて比較します。言語やカルチャに依存しないため、プログラム内部の識別子やキーの比較に向いています。
C#string a = "file";
string b = "FILE";
bool result = string.Equals(a, b, StringComparison.Ordinal);
Console.WriteLine(result); // False
StringComparison.OrdinalIgnoreCaseを使うと、大文字小文字を無視して比較できます。
C#string a = "file";
string b = "FILE";
bool result = string.Equals(a, b, StringComparison.OrdinalIgnoreCase);
Console.WriteLine(result); // True
一方、StringComparison.CurrentCultureは、現在のカルチャに基づいて比較します。ユーザーに見える自然言語の検索や並び替えでは、カルチャを考慮した比較が必要になる場合があります。
C#string text = "café";
bool result = text.Contains("é", StringComparison.CurrentCulture);
Console.WriteLine(result); // True
Microsoftのドキュメントでは、StringComparisonによってカルチャ依存の比較、インバリアントカルチャ、序数比較、大文字小文字の無視などを選択できることが説明されています。Microsoft Learn+1
7-2. OrdinalIgnoreCaseを使うべき場面
OrdinalIgnoreCaseは、カルチャに依存せず、大文字小文字だけを無視したい場合に使います。
たとえば、次のような文字列検索に向いています。
ファイル拡張子
URL
メールドメイン
設定キー
識別子
ログレベル
コマンド名
APIパラメータ
コード例です。
C#string fileName = "REPORT.CSV";
bool isCsv = fileName.EndsWith(".csv", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(isCsv); // True
C#string log = "error: file not found";
bool isError = log.StartsWith("ERROR", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(isError); // True
StringComparer.OrdinalIgnoreCaseについて、Microsoftの公式ドキュメントでは、言語に依存しない単純なバイト比較に適しており、パスやファイル名のような大文字小文字を無視するリソースに適していると説明されています。Microsoft Learn
7-3. 日本語文字列検索で注意すべきポイント
日本語の文字列検索では、次のような表記揺れに注意が必要です。
全角と半角
ひらがなとカタカナ
旧字体と新字体
長音符の有無
スペースの有無
記号の違い
大小文字の違い
たとえば、次の文字列は見た目が似ていても、単純なContainsでは一致しない場合があります。
C#string text = "カタカナ";
string keyword = "カタカナ";
bool result = text.Contains(keyword, StringComparison.Ordinal);
Console.WriteLine(result); // False
半角カタカナと全角カタカナは異なる文字として扱われます。
また、スペースの違いにも注意が必要です。
C#string text = "C# 文字列検索"; // 全角スペース
string keyword = "C# 文字列検索"; // 半角スペース
bool result = text.Contains(keyword, StringComparison.Ordinal);
Console.WriteLine(result); // False
日本語検索では、検索前に文字列を正規化する、全角半角を統一する、空白を除去するなど、仕様に応じた前処理が必要になることがあります。
7-4. 全角半角・ひらがなカタカナの揺れに対応する考え方
全角半角やひらがなカタカナの揺れに対応するには、検索前に表記をそろえるのが基本です。
たとえば、空白を除去して検索する例です。
C#string NormalizeSpace(string value)
{
return value
.Replace(" ", "")
.Replace(" ", "");
}
string text = "C# 文字列 検索";
string keyword = "C#文字列検索";
bool result = NormalizeSpace(text).Contains(
NormalizeSpace(keyword),
StringComparison.OrdinalIgnoreCase);
Console.WriteLine(result); // True
Unicode正規化を使う方法もあります。
C#using System.Text;
string text = "カタカナ";
string normalized = text.Normalize(NormalizationForm.FormKC);
Console.WriteLine(normalized); // カタカナ
NormalizationForm.FormKCを使うと、互換文字を正規化できます。ただし、すべての表記揺れを自動的に解決できるわけではありません。
ひらがなとカタカナを同一視したい場合は、別途変換処理を用意することがあります。
C#public static string HiraganaToKatakana(string value)
{
char[] chars = value.ToCharArray();
for (int i = 0; i < chars.Length; i++)
{
if (chars[i] >= 'ぁ' && chars[i] <= 'ん')
{
chars[i] = (char)(chars[i] + 0x60);
}
}
return new string(chars);
}
使用例です。
C#string text = "かたかな";
string keyword = "カタカナ";
bool result = HiraganaToKatakana(text).Contains(
HiraganaToKatakana(keyword),
StringComparison.Ordinal);
Console.WriteLine(result); // True
日本語検索では、「どの揺れを同一視するか」を先に決めることが重要です。
7-5. ToLowerやToUpperで比較する方法が非推奨になりやすい理由
大文字小文字を無視するために、次のようなコードを書くことがあります。
C#string text = "Hello C#";
bool result = text.ToLower().Contains("hello");
この方法は一見わかりやすいですが、次のような問題があります。
余計な文字列が生成される
カルチャの影響を受ける場合がある
比較意図が明確になりにくい
null対策が別途必要
よりよい書き方は、StringComparison.OrdinalIgnoreCaseを指定する方法です。
C#string text = "Hello C#";
bool result = text.Contains("hello", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(result); // True
IndexOfでも同じです。
C#string text = "Hello C#";
bool result = text.IndexOf("hello", StringComparison.OrdinalIgnoreCase) >= 0;
Console.WriteLine(result); // True
大文字小文字を無視したい場合は、ToLowerやToUpperで変換してから比較するのではなく、比較メソッドにStringComparisonを指定するのが基本です。
8. C#の文字列検索を高速化するコツ
文字列検索は、少量のデータではほとんど問題になりません。しかし、大量のログ、CSV、検索対象リストを扱う場合は、処理速度を意識する必要があります。
8-1. Contains・IndexOf・Regexの速度の違い
一般的に、単純な文字列検索ではContainsやIndexOfのほうが、正規表現よりもシンプルで高速になりやすいです。
C#// 単純なキーワード検索
bool result = text.Contains("error", StringComparison.OrdinalIgnoreCase);
正規表現でも同じことはできます。
C#using System.Text.RegularExpressions;
bool result = Regex.IsMatch(text, "error", RegexOptions.IgnoreCase);
しかし、単純にキーワードを探すだけなら、正規表現を使う必要はありません。正規表現はパターン解析のための仕組みであり、単純検索ではオーバーヘッドが大きくなりやすいからです。
Microsoftのコード分析ルールでも、Regex.Match(...).Successで一致判定するよりRegex.IsMatchを使うほうがシンプルで高速だと説明されています。Microsoft Learn
8-2. 単純検索ではRegexよりContainsやIndexOfを使う
特定キーワードが含まれるかどうかだけを調べるなら、Containsを使います。
C#bool hasError = text.Contains("error", StringComparison.OrdinalIgnoreCase);
位置が必要ならIndexOfを使います。
C#int index = text.IndexOf("error", StringComparison.OrdinalIgnoreCase);
if (index >= 0)
{
Console.WriteLine($"見つかった位置: {index}");
}
前方一致ならStartsWithです。
C#bool isError = text.StartsWith("ERROR", StringComparison.OrdinalIgnoreCase);
後方一致ならEndsWithです。
C#bool isCsv = fileName.EndsWith(".csv", StringComparison.OrdinalIgnoreCase);
次のような正規表現は、単純検索では避けたほうがよいです。
C#// 単純検索には大げさ
bool result = Regex.IsMatch(text, "error", RegexOptions.IgnoreCase);
正規表現は、数字、日付、メールアドレス、複雑なログ形式など、パターン検索が必要な場合に使いましょう。
8-3. StringComparison.Ordinalを使って高速化する
カルチャを考慮する必要がない文字列検索では、StringComparison.OrdinalまたはStringComparison.OrdinalIgnoreCaseを使うのが効果的です。
C#bool result = text.Contains("keyword", StringComparison.Ordinal);
大文字小文字を無視する場合です。
C#bool result = text.Contains("keyword", StringComparison.OrdinalIgnoreCase);
Ordinal系の比較は、言語的なルールではなく文字コードに基づく比較です。ユーザー向けの自然言語検索ではなく、プログラム内部のキー、識別子、URL、ファイル名、ログなどを検索する場合に向いています。
C#string url = "https://example.com/api";
bool isApi = url.Contains("/api", StringComparison.OrdinalIgnoreCase);
比較ルールを明示することで、パフォーマンスだけでなく、コードの意図も明確になります。
8-4. 繰り返し検索ではRegexをコンパイルする
同じ正規表現を何度も使う場合は、Regexオブジェクトを再利用する方法があります。
C#using System.Text.RegularExpressions;
Regex regex = new Regex(@"\d{4}/\d{2}/\d{2}");
string[] texts =
{
"日付: 2026/06/18",
"期限: 2026/07/01",
"日付なし"
};
foreach (string text in texts)
{
if (regex.IsMatch(text))
{
Console.WriteLine(text);
}
}
さらに、繰り返し使用する正規表現ではRegexOptions.Compiledを指定することもできます。
C#using System.Text.RegularExpressions;
Regex regex = new Regex(
@"\d{4}/\d{2}/\d{2}",
RegexOptions.Compiled);
bool result = regex.IsMatch("期限: 2026/06/18");
Console.WriteLine(result); // True
ただし、RegexOptions.Compiledは常に速くなるわけではありません。コンパイル自体にもコストがあるため、短い処理や一度しか使わない正規表現では逆効果になることもあります。
繰り返し大量に実行する正規表現だけ、コンパイルや再利用を検討しましょう。
8-5. 大量データ検索でLINQを使うときの注意点
LINQは便利ですが、大量データを検索するときは使い方に注意が必要です。
たとえば、次のコードでは条件に一致するすべての要素をリスト化します。
C#List<string> results = logs
.Where(log => log.Contains("error", StringComparison.OrdinalIgnoreCase))
.ToList();
検索結果をすべて保持したい場合は問題ありませんが、存在するかどうかだけ知りたい場合はAnyを使うほうが効率的です。
C#bool exists = logs.Any(log =>
log.Contains("error", StringComparison.OrdinalIgnoreCase));
最初の1件だけ欲しい場合はFirstOrDefaultを使います。
C#string? firstError = logs.FirstOrDefault(log =>
log.Contains("error", StringComparison.OrdinalIgnoreCase));
不要なToListを避けることも大切です。
C#// 必要になるまで列挙されない
IEnumerable<string> query = logs.Where(log =>
log.Contains("error", StringComparison.OrdinalIgnoreCase));
大量データでは、次の点を意識しましょう。
存在判定だけならAny
最初の1件だけならFirstOrDefault
全件必要なときだけToList
単純検索ならRegexを避ける
比較ルールはOrdinal系を優先する
8-6. Span・Memoryを使った高速化の考え方
より高度な高速化が必要な場合は、Span<char>やReadOnlySpan<char>を使う方法もあります。
ReadOnlySpan<char>を使うと、部分文字列を作成せずに文字列の一部を参照できます。
C#string text = "ERROR: File not found";
ReadOnlySpan<char> span = text.AsSpan();
bool startsWithError = span.StartsWith(
"ERROR".AsSpan(),
StringComparison.Ordinal);
Console.WriteLine(startsWithError); // True
通常、Substringを使うと新しい文字列が作成されます。
C#string text = "ERROR: File not found";
string prefix = text.Substring(0, 5);
Console.WriteLine(prefix); // ERROR
一方、Spanを使うと、元の文字列を参照したまま処理できます。
C#string text = "ERROR: File not found";
ReadOnlySpan<char> prefix = text.AsSpan(0, 5);
bool result = prefix.SequenceEqual("ERROR".AsSpan());
Console.WriteLine(result); // True
ただし、SpanやMemoryを使った最適化は、通常のアプリケーションでは必要ないことも多いです。まずはContains、IndexOf、StringComparisonを適切に使い、それでもパフォーマンスが問題になる場合に検討しましょう。
9. 文字列検索でよくあるエラーと対処法
C#の文字列検索では、null、戻り値判定、正規表現のエスケープ、日本語や記号の扱いなどでミスが起こりやすいです。
9-1. NullReferenceExceptionを防ぐ方法
検索対象がnullの状態でContainsやIndexOfを呼び出すと、NullReferenceExceptionが発生します。
C#string? text = null;
// 例外が発生する
bool result = text.Contains("test");
対策として、nullチェックを行います。
C#string? text = null;
bool result = text != null
&& text.Contains("test", StringComparison.Ordinal);
Console.WriteLine(result); // False
null条件演算子を使う方法もあります。
C#string? text = null;
bool result = text?.Contains("test", StringComparison.Ordinal) == true;
Console.WriteLine(result); // False
検索処理を共通化するなら、ヘルパーメソッドにしておくと便利です。
C#public static bool ContainsIgnoreCase(string? source, string? keyword)
{
return !string.IsNullOrEmpty(source)
&& !string.IsNullOrEmpty(keyword)
&& source.Contains(keyword, StringComparison.OrdinalIgnoreCase);
}
9-2. IndexOfの戻り値判定ミスを防ぐ方法
IndexOfでよくあるミスが、戻り値を> 0で判定してしまうことです。
C#string text = "error message";
if (text.IndexOf("error", StringComparison.OrdinalIgnoreCase) > 0)
{
Console.WriteLine("見つかりました");
}
このコードでは、"error"が先頭にある場合、IndexOfは0を返します。そのため、> 0では条件を満たさず、見つからなかった扱いになります。
正しくは、>= 0または!= -1で判定します。
C#string text = "error message";
if (text.IndexOf("error", StringComparison.OrdinalIgnoreCase) >= 0)
{
Console.WriteLine("見つかりました");
}
存在判定だけなら、Containsを使うとミスを減らせます。
C#string text = "error message";
if (text.Contains("error", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("見つかりました");
}
9-3. 正規表現のエスケープミスを防ぐ方法
正規表現では、\dや\sのようにバックスラッシュを使います。C#の通常文字列ではバックスラッシュ自体もエスケープが必要です。
C#using System.Text.RegularExpressions;
bool result = Regex.IsMatch("abc123", "\\d+");
このように"\\d+"と書く必要があります。
ただし、C#では逐語的文字列リテラルを使うと、正規表現を読みやすく書けます。
C#using System.Text.RegularExpressions;
bool result = Regex.IsMatch("abc123", @"\d+");
正規表現では、.、*、+、?、(、)、[、]などが特別な意味を持ちます。通常の文字として検索したい場合は、エスケープが必要です。
C#using System.Text.RegularExpressions;
string text = "price is 1.23";
bool result = Regex.IsMatch(text, @"1\.23");
Console.WriteLine(result); // True
ユーザー入力を正規表現パターンとしてではなく、通常の文字列として検索したい場合は、Regex.Escapeを使います。
C#using System.Text.RegularExpressions;
string input = "1.23";
string text = "price is 1.23";
string pattern = Regex.Escape(input);
bool result = Regex.IsMatch(text, pattern);
Console.WriteLine(result); // True
9-4. 日本語や記号を検索できないときの確認ポイント
日本語や記号を検索できないときは、まず文字列が本当に同じかを確認しましょう。
よくある原因は次のとおりです。
全角と半角が違う
ひらがなとカタカナが違う
空白が半角と全角で違う
似た記号だが別文字
改行コードが違う
Unicode正規化形式が違う
前後に見えない空白がある
たとえば、全角スペースと半角スペースは別文字です。
C#string a = "C# 文字列検索"; // 半角スペース
string b = "C# 文字列検索"; // 全角スペース
bool result = a == b;
Console.WriteLine(result); // False
前後の空白が原因ならTrimを使います。
C#string input = " keyword ";
bool result = input.Trim().Equals(
"keyword",
StringComparison.OrdinalIgnoreCase);
Console.WriteLine(result); // True
改行やタブを含む場合は、表示して確認しづらいため、デバッグ時に文字コードや文字数を確認すると原因を見つけやすくなります。
C#string text = "C# 文字列検索";
foreach (char c in text)
{
Console.WriteLine($"{c} : U+{(int)c:X4}");
}
日本語検索では、単純なContainsだけで対応できる範囲と、正規化や変換が必要な範囲を分けて考えることが大切です。
9-5. パフォーマンスが悪いときの見直しポイント
文字列検索のパフォーマンスが悪いときは、次の点を確認しましょう。
単純検索にRegexを使っていないか
ループ内で毎回ToLowerやToUpperを呼んでいないか
不要なSubstringで文字列を大量生成していないか
LINQで不要なToListを呼んでいないか
存在確認だけなのに全件検索していないか
StringComparisonを明示しているか
同じRegexを何度も生成していないか
悪い例です。
C#foreach (string log in logs)
{
if (Regex.IsMatch(log, "error", RegexOptions.IgnoreCase))
{
Console.WriteLine(log);
}
}
単純検索なら、Containsを使うほうがシンプルです。
C#foreach (string log in logs)
{
if (log.Contains("error", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine(log);
}
}
大文字小文字を無視するために、ループ内で毎回ToLowerするのも避けたい書き方です。
C#foreach (string log in logs)
{
if (log.ToLower().Contains("error"))
{
Console.WriteLine(log);
}
}
改善例です。
C#foreach (string log in logs)
{
if (log.Contains("error", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine(log);
}
}
大量データを扱う場合は、まず単純なメソッドで書き、必要になった段階でRegexの再利用、Span、インデックス設計、検索対象の事前整形などを検討しましょう。
まとめ
C#で文字列検索を行う方法は複数ありますが、基本的な使い分けはシンプルです。
指定した文字列が含まれているかだけを調べたい場合はContains、見つかった位置が必要な場合はIndexOf、後ろから探したい場合はLastIndexOfを使います。先頭一致ならStartsWith、末尾一致ならEndsWithが適しています。数字、メールアドレス、日付、複雑なログ形式のようなパターン検索には、Regexを使います。
また、C#の文字列検索ではStringComparisonの指定が重要です。プログラム内部のキー、URL、ファイル名、ログ、識別子などでは、StringComparison.OrdinalまたはStringComparison.OrdinalIgnoreCaseを使うと、意図が明確で安定した検索になります。
一方、日本語文字列では、全角半角、ひらがなカタカナ、空白、記号、Unicode正規化などに注意が必要です。必要に応じて検索前に文字列を正規化し、表記揺れを吸収しましょう。
パフォーマンス面では、単純検索に正規表現を使いすぎないことが大切です。まずはContains、IndexOf、StartsWith、EndsWithを適切に使い、複雑な条件が必要なときだけRegexを使うようにすると、読みやすく高速なコードになります。
C#の文字列検索では、次の方針を押さえておくと実装しやすくなります。
含まれるかだけならContains
位置が必要ならIndexOf
後ろから探すならLastIndexOf
先頭一致ならStartsWith
末尾一致ならEndsWith
複雑なパターンならRegex
比較ルールはStringComparisonで明示
単純検索ではRegexを避ける
nullと空文字を必ず考慮する
日本語検索では表記揺れに注意する
これらを使い分ければ、C#での文字列検索を安全かつ効率的に実装できます。

