C# IndexOfの使い方を完全解説|文字列検索・戻り値・見つからない時の対処法まで

はじめに

C#で文字列の中から特定の文字や単語を探したいときによく使うのが、IndexOfメソッドです。

たとえば、次のような処理で使われます。

C#
string text = "Hello C#";
int index = text.IndexOf("C#");

Console.WriteLine(index); // 6

IndexOfは、検索した文字列がどの位置にあるかを数値で返します。見つからない場合は-1を返すため、条件分岐にもよく使われます。

この記事では、C#のIndexOfの基本的な使い方から、戻り値の意味、見つからないときの原因、大文字・小文字を区別しない検索、配列やListでの使い方まで、初心者にもわかりやすく解説します。

1. C#のIndexOfとは?文字列や配列から位置を検索する基本メソッド

IndexOfとは、対象の中から指定した値を検索し、最初に見つかった位置を返すメソッドです。

C#では主に、次のような場面で使われます。

C#
string text = "apple";
int index = text.IndexOf("p");

Console.WriteLine(index); // 1

この例では、文字列"apple"の中から"p"を探しています。"p"は先頭から数えて1番目の位置にあるため、戻り値は1になります。

C#のインデックスは0から始まります。つまり、最初の文字は0番目、2文字目は1番目です。

1-1. IndexOfでできること

IndexOfを使うと、次のような検索ができます。

C#
string text = "C# programming";

Console.WriteLine(text.IndexOf('C')); // 0
Console.WriteLine(text.IndexOf("#")); // 1
Console.WriteLine(text.IndexOf("program")); // 3

文字1文字だけでなく、単語や複数文字の文字列も検索できます。

また、文字列だけでなく、配列やList<T>でもIndexOfを使えます。

C#
int[] numbers = { 10, 20, 30 };
int index = Array.IndexOf(numbers, 20);

Console.WriteLine(index); // 1

このように、IndexOfは「指定した値がどこにあるか」を調べるための基本的なメソッドです。

1-2. 戻り値は「見つかった位置」または「-1」

IndexOfの戻り値はint型です。

検索対象が見つかった場合は、その位置を返します。見つからなかった場合は-1を返します。

C#
string text = "Hello";

Console.WriteLine(text.IndexOf("H")); // 0
Console.WriteLine(text.IndexOf("e")); // 1
Console.WriteLine(text.IndexOf("x")); // -1

"x""Hello"の中に存在しないため、戻り値は-1になります。

この-1を使って、検索結果を判定することがよくあります。

C#
if (text.IndexOf("e") != -1)
{
Console.WriteLine("見つかりました");
}

1-3. 文字列検索でよく使われる理由

IndexOfがよく使われる理由は、検索した文字列が「含まれているか」だけでなく、「どの位置にあるか」まで取得できるからです。

たとえば、メールアドレスの@の位置を調べたい場合は、次のように書けます。

C#
string email = "user@example.com";
int atIndex = email.IndexOf("@");

Console.WriteLine(atIndex); // 4

位置がわかれば、Substringなどと組み合わせて文字列を切り出すこともできます。

C#
string userName = email.Substring(0, atIndex);
Console.WriteLine(userName); // user

このように、IndexOfは文字列の確認だけでなく、文字列加工にも役立ちます。

1-4. Containsとの違い

IndexOfと似たメソッドにContainsがあります。

Containsは、指定した文字列が含まれているかどうかをtrueまたはfalseで返します。

C#
string text = "Hello C#";

Console.WriteLine(text.Contains("C#")); // True
Console.WriteLine(text.IndexOf("C#")); // 6

違いを整理すると、次のようになります。

メソッド戻り値用途
IndexOf見つかった位置、または-1位置を知りたいとき
Containstrueまたはfalse含まれているかだけ知りたいとき

単に「含まれているか」を確認するだけならContainsの方が読みやすいです。一方で、位置を使って処理したい場合はIndexOfを使います。

2. C# IndexOfの基本的な使い方

ここからは、C#でIndexOfを使う基本構文を確認していきます。

文字を探す場合、文字列を探す場合、検索開始位置を指定する場合など、実務でもよく使うパターンを順番に見ていきましょう。

2-1. 文字列から特定の文字を探す

特定の1文字を探す場合は、IndexOf(char)を使います。

C#
string text = "banana";
int index = text.IndexOf('a');

Console.WriteLine(index); // 1

"banana"の中で最初に'a'が出てくる位置は1です。

複数回出現する文字でも、IndexOfは最初に見つかった位置だけを返します。

C#
string text = "banana";

Console.WriteLine(text.IndexOf('a')); // 1
Console.WriteLine(text.IndexOf('n')); // 2

2-2. 文字列から特定の単語・文字列を探す

単語や複数文字の文字列を探す場合は、IndexOf(string)を使います。

C#
string text = "I like C# programming.";
int index = text.IndexOf("C#");

Console.WriteLine(index); // 7

"C#"が7番目の位置から始まっているため、戻り値は7です。

検索対象が見つからない場合は-1になります。

C#
string text = "I like C# programming.";
int index = text.IndexOf("Java");

Console.WriteLine(index); // -1

2-3. 検索開始位置を指定する

IndexOfでは、検索を開始する位置を指定できます。

C#
string text = "abc abc abc";

int first = text.IndexOf("abc");
int second = text.IndexOf("abc", first + 1);

Console.WriteLine(first); // 0
Console.WriteLine(second); // 4

最初のIndexOf("abc")では、先頭から検索するため0が返ります。

次のIndexOf("abc", first + 1)では、1番目以降から検索するため、2つ目の"abc"が見つかり、4が返ります。

同じ文字列が複数回出現する場合、検索開始位置を指定すると2回目以降の出現位置を探せます。

2-4. 検索範囲を指定する

検索開始位置だけでなく、検索する文字数を指定することもできます。

C#
string text = "abc def ghi";

int index = text.IndexOf("g", 0, 7);

Console.WriteLine(index); // -1

この例では、0番目から7文字分だけを検索しています。"g"は8番目にあるため、指定した範囲内では見つからず、-1になります。

範囲を広げると見つかります。

C#
string text = "abc def ghi";

int index = text.IndexOf("g", 0, text.Length);

Console.WriteLine(index); // 8

検索範囲を指定する overload は、文字列の一部だけを対象に検索したい場合に便利です。

2-5. サンプルコードで基本構文を確認する

IndexOfの基本構文は次のとおりです。

C#
文字列.IndexOf(検索する文字または文字列);

実際のコードで確認してみましょう。

C#
string message = "Welcome to C# world";

int index1 = message.IndexOf('C');
int index2 = message.IndexOf("world");
int index3 = message.IndexOf("Java");

Console.WriteLine(index1); // 11
Console.WriteLine(index2); // 14
Console.WriteLine(index3); // -1

IndexOfは、最初に見つかった位置を返します。見つからなければ-1です。

この基本を理解しておけば、文字列検索の多くの処理に応用できます。

3. IndexOfの戻り値を正しく理解する

IndexOfを使ううえで特に重要なのが、戻り値の理解です。

IndexOftruefalseを返すメソッドではありません。戻り値は数値です。

そのため、判定方法を間違えるとバグの原因になります。

3-1. 見つかった場合は0から始まるインデックスを返す

C#の文字列の位置は0から始まります。

C#
string text = "ABCDE";

Console.WriteLine(text.IndexOf("A")); // 0
Console.WriteLine(text.IndexOf("B")); // 1
Console.WriteLine(text.IndexOf("C")); // 2

"A"は先頭にあるため、戻り値は0です。

ここで注意したいのは、0も「見つかった」という意味だという点です。

3-2. 見つからない場合は-1を返す

検索した文字や文字列が存在しない場合、IndexOf-1を返します。

C#
string text = "ABCDE";

int index = text.IndexOf("Z");

Console.WriteLine(index); // -1

そのため、見つかったかどうかを判定する場合は、次のように書きます。

C#
if (text.IndexOf("Z") == -1)
{
Console.WriteLine("見つかりませんでした");
}

または、見つかった場合の処理を書くなら次のようにします。

C#
if (text.IndexOf("A") != -1)
{
Console.WriteLine("見つかりました");
}

3-3. 先頭にある場合は0を返す点に注意

IndexOfで最もよくあるミスが、戻り値0を「見つからなかった」と勘違いすることです。

C#
string text = "C# programming";

int index = text.IndexOf("C#");

Console.WriteLine(index); // 0

"C#"は先頭にあるため、戻り値は0です。

0は見つかった位置を表しています。見つからない場合は必ず-1です。

3-4. if文で検索結果を判定する方法

IndexOfで検索結果を判定する場合は、-1と比較するのが基本です。

C#
string text = "Hello C#";

if (text.IndexOf("C#") >= 0)
{
Console.WriteLine("C#が含まれています");
}

>= 0と書けば、0番目に見つかった場合も正しく判定できます。

次のように> 0と書くと、先頭にある場合を見逃してしまいます。

C#
string text = "C# Hello";

if (text.IndexOf("C#") > 0)
{
Console.WriteLine("見つかりました");
}

このコードでは、"C#"が先頭にあるためIndexOf0を返します。しかし条件が> 0なので、処理は実行されません。

正しくは次のように書きます。

C#
if (text.IndexOf("C#") >= 0)
{
Console.WriteLine("見つかりました");
}

3-5. -1判定でよくあるミス

IndexOfでよくあるミスは次の3つです。

1つ目は、> 0で判定してしまうことです。

C#
if (text.IndexOf("abc") > 0)
{
Console.WriteLine("見つかりました");
}

この書き方では、先頭に"abc"がある場合に判定できません。

2つ目は、0を見つからない値だと思ってしまうことです。

C#
int index = text.IndexOf("abc");

if (index == 0)
{
Console.WriteLine("見つからない");
}

これは誤りです。0は先頭で見つかったことを意味します。

3つ目は、IndexOfの結果をそのまま真偽値のように扱おうとすることです。

C#では、intをそのままif条件には使えません。

C#
// C#ではコンパイルエラー
// if (text.IndexOf("abc"))
// {
// }

必ず-10と比較して判定しましょう。

4. 文字列検索で使うIndexOfの実践例

ここでは、実際の開発でよく使うIndexOfの例を紹介します。

文字列に特定の文字が含まれるか、メールアドレスやURLをチェックする方法、区切り文字の位置を取得する方法などを見ていきましょう。

4-1. 特定の文字が含まれているか確認する

文字列に特定の文字が含まれているか確認するには、IndexOfの戻り値が-1でないかを判定します。

C#
string text = "apple";

if (text.IndexOf('p') != -1)
{
Console.WriteLine("pが含まれています");
}
else
{
Console.WriteLine("pは含まれていません");
}

より読みやすく書くなら、次のように>= 0を使う方法もあります。

C#
if (text.IndexOf('p') >= 0)
{
Console.WriteLine("pが含まれています");
}

4-2. メールアドレスに@が含まれるか判定する

メールアドレスに@が含まれているかを確認する例です。

C#
string email = "user@example.com";

if (email.IndexOf('@') >= 0)
{
Console.WriteLine("@が含まれています");
}
else
{
Console.WriteLine("@が含まれていません");
}

@の位置を取得すれば、ユーザー名部分とドメイン部分を分けることもできます。

C#
string email = "user@example.com";

int atIndex = email.IndexOf('@');

if (atIndex >= 0)
{
string userName = email.Substring(0, atIndex);
string domain = email.Substring(atIndex + 1);

Console.WriteLine(userName); // user
Console.WriteLine(domain); // example.com
}

ただし、実際にメールアドレスとして正しいかを厳密に検証するには、@の有無だけでは不十分です。ここではあくまで簡単な文字列チェックの例として考えてください。

4-3. URLに特定の文字列が含まれるか調べる

URLに特定の文字列が含まれるかを調べる場合にもIndexOfを使えます。

C#
string url = "https://example.com/products?id=100";

if (url.IndexOf("products") >= 0)
{
Console.WriteLine("商品ページのURLです");
}

クエリ文字列が含まれているかを確認する例です。

C#
string url = "https://example.com/search?q=csharp";

if (url.IndexOf("?") >= 0)
{
Console.WriteLine("クエリ文字列があります");
}

ただし、URLの解析を本格的に行う場合は、Uriクラスを使う方が安全です。

C#
Uri uri = new Uri("https://example.com/search?q=csharp");

Console.WriteLine(uri.Host); // example.com
Console.WriteLine(uri.Query); // ?q=csharp

IndexOfは簡単なチェックには便利ですが、複雑な構造を持つ文字列では専用のクラスやライブラリを使うことも検討しましょう。

4-4. カンマや区切り文字の位置を取得する

CSVのようにカンマで区切られた文字列から、区切り文字の位置を取得する例です。

C#
string data = "apple,banana,orange";

int commaIndex = data.IndexOf(',');

Console.WriteLine(commaIndex); // 5

最初のカンマの位置が取得できます。

その位置を使って、最初の値だけを取り出すこともできます。

C#
string data = "apple,banana,orange";

int commaIndex = data.IndexOf(',');

if (commaIndex >= 0)
{
string firstItem = data.Substring(0, commaIndex);
Console.WriteLine(firstItem); // apple
}

ただし、CSVを本格的に解析する場合は、カンマだけで単純に分割できないケースもあります。引用符やエスケープが含まれる場合は、CSV用のパーサーを使う方が安全です。

4-5. IndexOfとSubstringを組み合わせて文字列を切り出す

IndexOfは、Substringと組み合わせることで文字列の切り出しに使えます。

次の例では、:より後ろの文字列を取得します。

C#
string text = "Name:Tanaka";

int colonIndex = text.IndexOf(':');

if (colonIndex >= 0)
{
string value = text.Substring(colonIndex + 1);
Console.WriteLine(value); // Tanaka
}

IndexOfで位置を取得し、その位置を基準にSubstringで切り出しています。

前半部分を取り出す場合は次のようにします。

C#
string text = "Name:Tanaka";

int colonIndex = text.IndexOf(':');

if (colonIndex >= 0)
{
string key = text.Substring(0, colonIndex);
Console.WriteLine(key); // Name
}

IndexOfSubstringを組み合わせると柔軟な文字列処理ができますが、IndexOf-1を返した状態でSubstringに使うと例外の原因になります。

必ずindex >= 0を確認してから切り出しましょう。

5. 大文字・小文字を区別せずにIndexOfで検索する方法

C#で文字列検索を行うとき、大文字・小文字の扱いは重要です。

たとえば、"CSharp"の中から"csharp"を検索したい場合、通常の比較では見つからないことがあります。

5-1. 通常のIndexOfは大文字・小文字を区別する

通常のIndexOfでは、大文字と小文字が区別されます。

C#
string text = "CSharp";

Console.WriteLine(text.IndexOf("CSharp")); // 0
Console.WriteLine(text.IndexOf("csharp")); // -1

"CSharp""csharp"は大文字・小文字が異なるため、同じ文字列とは判定されません。

そのため、ユーザー入力やURL、検索キーワードなどを扱う場合には注意が必要です。

5-2. StringComparison.OrdinalIgnoreCaseを使う

大文字・小文字を区別せずに検索したい場合は、StringComparison.OrdinalIgnoreCaseを指定します。

C#
string text = "CSharp";

int index = text.IndexOf("csharp", StringComparison.OrdinalIgnoreCase);

Console.WriteLine(index); // 0

OrdinalIgnoreCaseは、カルチャに依存しない比較で、大文字・小文字を無視します。

実務では、単純な識別子、URL、キー、コード値などの比較では、次のように書くことが多いです。

C#
if (text.IndexOf("csharp", StringComparison.OrdinalIgnoreCase) >= 0)
{
Console.WriteLine("見つかりました");
}

5-3. ToLowerやToUpperで比較する方法との違い

大文字・小文字を無視する方法として、ToLowerToUpperを使う方法もあります。

C#
string text = "CSharp";

if (text.ToLower().IndexOf("csharp") >= 0)
{
Console.WriteLine("見つかりました");
}

この方法でも動作しますが、実務ではあまり推奨されません。

理由は、検索のためだけに新しい文字列を作ることになり、無駄な処理が増える可能性があるためです。また、カルチャによって大文字・小文字変換の結果が変わるケースもあります。

そのため、次のようにStringComparisonを指定する方が意図が明確です。

C#
string text = "CSharp";

if (text.IndexOf("csharp", StringComparison.OrdinalIgnoreCase) >= 0)
{
Console.WriteLine("見つかりました");
}

5-4. 日本語やカルチャ依存の比較で注意すること

日本語を含む文字列でもIndexOfは使えます。

C#
string text = "今日は良い天気です";

int index = text.IndexOf("良い");

Console.WriteLine(index); // 3

ただし、文字列比較にはカルチャ依存の考え方があります。

たとえば、英字の大文字・小文字、アクセント記号、言語ごとの比較ルールなどが関係する場合は、どの比較方法を使うかを意識する必要があります。

代表的な指定は次のとおりです。

C#
string text = "CSharp";

// カルチャに依存しない比較
text.IndexOf("csharp", StringComparison.OrdinalIgnoreCase);

// 現在のカルチャに基づく比較
text.IndexOf("csharp", StringComparison.CurrentCultureIgnoreCase);

// インバリアントカルチャに基づく比較
text.IndexOf("csharp", StringComparison.InvariantCultureIgnoreCase);

プログラム内部の識別子や固定キーワードを検索する場合は、OrdinalまたはOrdinalIgnoreCaseが使われることが多いです。

人間が読む自然言語として比較したい場合は、カルチャを考慮した比較が適している場合もあります。

5-5. 実務で推奨される書き方

実務では、意図を明確にするためにStringComparisonを指定する書き方がおすすめです。

大文字・小文字を区別する場合は次のように書きます。

C#
string text = "Hello C#";

if (text.IndexOf("C#", StringComparison.Ordinal) >= 0)
{
Console.WriteLine("見つかりました");
}

大文字・小文字を区別しない場合は次のように書きます。

C#
string text = "Hello C#";

if (text.IndexOf("c#", StringComparison.OrdinalIgnoreCase) >= 0)
{
Console.WriteLine("見つかりました");
}

StringComparisonを明示すると、コードを読む人に「どのルールで比較したいのか」が伝わりやすくなります。

特に、実務コードでは次のような書き方がわかりやすいです。

C#
bool containsKeyword =
text.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0;

単に含まれているかどうかだけが必要なら、ContainsStringComparisonを指定する方法もあります。

C#
bool containsKeyword =
text.Contains(keyword, StringComparison.OrdinalIgnoreCase);

位置が必要ならIndexOf、真偽値だけでよいならContainsを使い分けましょう。

6. IndexOfで見つからない時の原因と対処法

IndexOfを使っていると、存在するはずの文字列が見つからず、-1が返ってくることがあります。

その場合は、単純に文字列が存在しないだけでなく、大文字・小文字、空白、全角・半角、改行コードなどが原因になっている可能性があります。

6-1. 検索対象の文字列が存在しない

最も単純な原因は、検索対象の文字列が実際には存在しないことです。

C#
string text = "Hello C#";

int index = text.IndexOf("Java");

Console.WriteLine(index); // -1

この場合、"Java"は含まれていないため-1になります。

まずは、検索対象の文字列と検索キーワードを確認しましょう。

C#
Console.WriteLine(text);
Console.WriteLine(keyword);

6-2. 大文字・小文字の違い

大文字・小文字の違いで見つからないケースもよくあります。

C#
string text = "Hello C#";

Console.WriteLine(text.IndexOf("hello")); // -1

"Hello""hello"は異なる文字列として扱われます。

大文字・小文字を無視したい場合は、StringComparison.OrdinalIgnoreCaseを使います。

C#
string text = "Hello C#";

int index = text.IndexOf("hello", StringComparison.OrdinalIgnoreCase);

Console.WriteLine(index); // 0

6-3. 全角・半角や空白の違い

見た目が似ていても、全角と半角が違うと別の文字として扱われます。

C#
string text = "C#入門";

Console.WriteLine(text.IndexOf("C#")); // -1

この例では、文字列内の"C#"は全角で、検索している"C#"は半角です。そのため一致しません。

また、空白にも注意が必要です。

C#
string text = "Hello  C#"; // HelloとC#の間に空白が2つ

Console.WriteLine(text.IndexOf("Hello C#")); // -1

見た目では気づきにくい余分な空白が含まれていると、検索に失敗することがあります。

必要に応じて、TrimReplaceで整形します。

C#
string text = "  Hello C#  ";
string trimmed = text.Trim();

Console.WriteLine(trimmed.IndexOf("Hello")); // 0

6-4. 改行コードや不可視文字が含まれている

改行コードやタブなどの不可視文字が原因で見つからないこともあります。

C#
string text = "Hello\r\nC#";

Console.WriteLine(text.IndexOf("Hello C#")); // -1

文字列の中には改行が入っているため、"Hello C#"とは一致しません。

改行を除去または置換すると検索できる場合があります。

C#
string text = "Hello\r\nC#";
string normalized = text.Replace("\r\n", " ");

Console.WriteLine(normalized.IndexOf("Hello C#")); // 0

タブが含まれている場合も同様です。

C#
string text = "Hello\tC#";

Console.WriteLine(text.IndexOf("Hello C#")); // -1

デバッグ時には、文字列をそのまま見るだけでなく、長さや文字コードも確認すると原因を見つけやすくなります。

6-5. nullや空文字を扱う場合の注意点

検索対象の文字列がnullの場合、IndexOfを呼び出すとNullReferenceExceptionが発生します。

C#
string text = null;

// 例外が発生する
// int index = text.IndexOf("C#");

nullの可能性がある場合は、事前にチェックしましょう。

C#
string text = null;

if (!string.IsNullOrEmpty(text) &&
text.IndexOf("C#", StringComparison.OrdinalIgnoreCase) >= 0)
{
Console.WriteLine("見つかりました");
}

検索する文字列が空文字の場合にも注意が必要です。

C#
string text = "Hello";

Console.WriteLine(text.IndexOf("")); // 0

空文字は先頭で見つかった扱いになり、通常は0が返ります。検索開始位置を指定した場合は、その開始位置が返ることがあります。

空文字を検索キーワードとして許可したくない場合は、事前にチェックしましょう。

C#
if (!string.IsNullOrEmpty(keyword))
{
int index = text.IndexOf(keyword);
}

6-6. デバッグで確認すべきポイント

IndexOfで思った結果にならない場合は、次のポイントを確認しましょう。

C#
Console.WriteLine($"text=[{text}]");
Console.WriteLine($"keyword=[{keyword}]");
Console.WriteLine($"text.Length={text.Length}");
Console.WriteLine($"keyword.Length={keyword.Length}");

角括弧で囲んで出力すると、前後の空白に気づきやすくなります。

C#
string text = " Hello ";
Console.WriteLine($"[{text}]"); // [ Hello ]

また、1文字ずつ確認すると不可視文字を発見しやすくなります。

C#
foreach (char c in text)
{
Console.WriteLine($"{c} : {(int)c}");
}

見つからない原因は、IndexOfの使い方ではなく、検索対象の文字列の中身にあることも多いです。

7. IndexOfの主なオーバーロード一覧

IndexOfには複数のオーバーロードがあります。

オーバーロードとは、同じメソッド名でも引数の種類や数が異なるメソッドのことです。

ここでは、よく使うIndexOfのオーバーロードを紹介します。

7-1. IndexOf(char)の使い方

IndexOf(char)は、1文字を検索する場合に使います。

C#
string text = "banana";

int index = text.IndexOf('n');

Console.WriteLine(index); // 2

文字を指定する場合は、シングルクォーテーションを使います。

C#
text.IndexOf('a');

文字列を指定する場合は、ダブルクォーテーションを使います。

C#
text.IndexOf("a");

1文字だけを探すなら、charを使う方が意図が明確です。

7-2. IndexOf(string)の使い方

IndexOf(string)は、文字列を検索する場合に使います。

C#
string text = "C# programming";

int index = text.IndexOf("program");

Console.WriteLine(index); // 3

単語や複数文字を探す場合はこちらを使います。

C#
string text = "abc def ghi";

Console.WriteLine(text.IndexOf("def")); // 4

ただし、比較ルールを明確にしたい場合は、StringComparisonを指定するオーバーロードを使うのがおすすめです。

7-3. IndexOf(char, int)の使い方

IndexOf(char, int)は、指定した位置から1文字を検索します。

C#
string text = "banana";

int index = text.IndexOf('a', 2);

Console.WriteLine(index); // 3

この例では、2番目の位置から'a'を検索しています。

"banana"の中で'a'は1番目、3番目、5番目にありますが、2番目以降で最初に見つかるのは3番目です。

7-4. IndexOf(string, int)の使い方

IndexOf(string, int)は、指定した位置から文字列を検索します。

C#
string text = "abc abc abc";

int index = text.IndexOf("abc", 1);

Console.WriteLine(index); // 4

0番目の"abc"は検索開始位置より前にあるため、次の"abc"が見つかります。

複数回出現する文字列を順番に探す場合に便利です。

C#
string text = "cat cat cat";
string keyword = "cat";

int index = text.IndexOf(keyword);

while (index >= 0)
{
Console.WriteLine(index);
index = text.IndexOf(keyword, index + keyword.Length);
}

実行結果は次のようになります。

C#
0
4
8

7-5. IndexOf(string, StringComparison)の使い方

IndexOf(string, StringComparison)は、比較方法を指定して文字列を検索します。

C#
string text = "Hello C#";

int index = text.IndexOf("hello", StringComparison.OrdinalIgnoreCase);

Console.WriteLine(index); // 0

大文字・小文字を無視する場合に便利です。

大文字・小文字を区別して、カルチャに依存しない比較をしたい場合は次のようにします。

C#
string text = "Hello C#";

int index = text.IndexOf("Hello", StringComparison.Ordinal);

Console.WriteLine(index); // 0

実務では、文字列検索の意図を明確にするために、StringComparisonを指定する書き方がよく使われます。

7-6. 用途別にどのオーバーロードを選ぶべきか

用途別にまとめると、次のようになります。

やりたいこと使うオーバーロード
1文字を探したいIndexOf(char)
文字列を探したいIndexOf(string)
指定位置以降から1文字を探したいIndexOf(char, int)
指定位置以降から文字列を探したいIndexOf(string, int)
大文字・小文字や比較ルールを指定したいIndexOf(string, StringComparison)

特に文字列検索では、比較ルールを明示するために次のような書き方が実務向きです。

C#
int index = text.IndexOf(keyword, StringComparison.OrdinalIgnoreCase);

位置が必要ない場合は、Containsを使う方が読みやすい場合もあります。

C#
bool exists = text.Contains(keyword, StringComparison.OrdinalIgnoreCase);

8. 配列やListでIndexOfを使う方法

IndexOfは文字列だけでなく、配列やList<T>でも使えます。

ただし、文字列のIndexOfとは呼び出し方や検索対象が少し異なります。

8-1. Array.IndexOfで配列内の要素位置を取得する

配列内の要素を検索する場合は、Array.IndexOfを使います。

C#
int[] numbers = { 10, 20, 30, 40 };

int index = Array.IndexOf(numbers, 30);

Console.WriteLine(index); // 2

30は配列の2番目の位置にあるため、戻り値は2です。

文字列の配列でも使えます。

C#
string[] fruits = { "apple", "banana", "orange" };

int index = Array.IndexOf(fruits, "banana");

Console.WriteLine(index); // 1

8-2. List<T>.IndexOfでリスト内を検索する

List<T>では、インスタンスメソッドとしてIndexOfを呼び出します。

C#
List<string> fruits = new List<string>
{
"apple",
"banana",
"orange"
};

int index = fruits.IndexOf("orange");

Console.WriteLine(index); // 2

List<T>.IndexOfも、最初に見つかった要素の位置を返します。

見つからない場合は-1です。

C#
int index = fruits.IndexOf("grape");

Console.WriteLine(index); // -1

8-3. 文字列のIndexOfとの違い

文字列のIndexOfは、文字や部分文字列を検索します。

C#
string text = "banana";
int index = text.IndexOf("na");

Console.WriteLine(index); // 2

一方、配列やListのIndexOfは、要素そのものを検索します。

C#
List<string> words = new List<string>
{
"banana",
"orange"
};

int index = words.IndexOf("banana");

Console.WriteLine(index); // 0

つまり、文字列の中の一部を探すのか、コレクション内の要素を探すのかが違います。

8-4. 見つからない場合の戻り値

配列やListでも、見つからない場合の戻り値は-1です。

C#
int[] numbers = { 1, 2, 3 };

int index = Array.IndexOf(numbers, 5);

Console.WriteLine(index); // -1

List<T>でも同じです。

C#
List<int> numbers = new List<int> { 1, 2, 3 };

int index = numbers.IndexOf(5);

Console.WriteLine(index); // -1

そのため、判定方法も文字列の場合と同じです。

C#
if (index >= 0)
{
Console.WriteLine("見つかりました");
}
else
{
Console.WriteLine("見つかりませんでした");
}

8-5. 独自クラスを検索する場合の注意点

独自クラスをList<T>.IndexOfで検索する場合は、等価比較に注意が必要です。

次の例を見てください。

C#
class User
{
public string Name { get; set; }
}
C#
List<User> users = new List<User>
{
new User { Name = "Tanaka" },
new User { Name = "Sato" }
};

User target = new User { Name = "Tanaka" };

int index = users.IndexOf(target);

Console.WriteLine(index); // -1

一見、Nameが同じなので見つかりそうですが、別のインスタンスなので一致しません。

独自クラスで値の内容をもとに比較したい場合は、Equalsをオーバーライドする、IEquatable<T>を実装する、またはLINQのFindIndexを使う方法があります。

C#
int index = users.FindIndex(user => user.Name == "Tanaka");

Console.WriteLine(index); // 0

条件を指定して検索したい場合は、IndexOfよりFindIndexの方が適しています。

9. IndexOfと関連メソッドの違い

C#には、IndexOf以外にも文字列検索に使えるメソッドがあります。

ここでは、LastIndexOfContainsStartsWithEndsWithRegexとの違いを整理します。

9-1. IndexOfとLastIndexOfの違い

IndexOfは、前から検索して最初に見つかった位置を返します。

C#
string text = "abc abc abc";

Console.WriteLine(text.IndexOf("abc")); // 0

一方、LastIndexOfは後ろから検索して、最後に見つかった位置を返します。

C#
string text = "abc abc abc";

Console.WriteLine(text.LastIndexOf("abc")); // 8

最初の出現位置を知りたい場合はIndexOf、最後の出現位置を知りたい場合はLastIndexOfを使います。

ファイルパスやURLの末尾側から区切り文字を探したいときは、LastIndexOfが便利です。

C#
string path = "/home/user/file.txt";

int index = path.LastIndexOf('/');

string fileName = path.Substring(index + 1);

Console.WriteLine(fileName); // file.txt

9-2. IndexOfとContainsの違い

Containsは、指定した文字列が含まれているかどうかを判定します。

C#
string text = "Hello C#";

Console.WriteLine(text.Contains("C#")); // True

IndexOfは、指定した文字列が見つかった位置を返します。

C#
string text = "Hello C#";

Console.WriteLine(text.IndexOf("C#")); // 6

含まれているかだけ知りたい場合はContains、位置が必要な場合はIndexOfを使いましょう。

C#
// 含まれているかだけ知りたい
if (text.Contains("C#"))
{
Console.WriteLine("含まれています");
}

// 位置も使いたい
int index = text.IndexOf("C#");
if (index >= 0)
{
Console.WriteLine($"位置: {index}");
}

9-3. IndexOfとStartsWithの違い

StartsWithは、文字列が指定した文字列で始まるかを判定します。

C#
string text = "https://example.com";

Console.WriteLine(text.StartsWith("https")); // True

IndexOfでも先頭にあるかを調べることはできます。

C#
Console.WriteLine(text.IndexOf("https") == 0); // True

しかし、先頭一致を確認したい場合はStartsWithの方が意図が明確です。

C#
if (text.StartsWith("https", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("HTTPSのURLです");
}

9-4. IndexOfとEndsWithの違い

EndsWithは、文字列が指定した文字列で終わるかを判定します。

C#
string fileName = "sample.txt";

Console.WriteLine(fileName.EndsWith(".txt")); // True

拡張子の確認などでは、IndexOfよりEndsWithの方が適しています。

C#
if (fileName.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("テキストファイルです");
}

文字列のどこかに含まれているかを調べたいならIndexOf、末尾にあるかを調べたいならEndsWithを使いましょう。

9-5. IndexOfと正規表現Regexの使い分け

IndexOfは、固定の文字列を検索するのに向いています。

C#
string text = "user@example.com";

if (text.IndexOf("@") >= 0)
{
Console.WriteLine("@が含まれています");
}

一方、正規表現Regexは、パターンに一致する文字列を検索する場合に使います。

C#
using System.Text.RegularExpressions;

string text = "商品番号: A-12345";

Match match = Regex.Match(text, @"[A-Z]-\d+");

if (match.Success)
{
Console.WriteLine(match.Value); // A-12345
}

使い分けの目安は次のとおりです。

用途向いている方法
固定文字列を探すIndexOf
含まれるかだけ調べるContains
先頭一致を調べるStartsWith
末尾一致を調べるEndsWith
複雑なパターンを探すRegex

単純な検索ならIndexOfの方が簡潔で読みやすいです。複雑な条件で検索したい場合はRegexを使いましょう。

10. IndexOfを使う時の注意点とベストプラクティス

IndexOfは便利なメソッドですが、使い方を間違えるとバグの原因になります。

ここでは、実務で意識したい注意点とベストプラクティスを紹介します。

10-1. 戻り値0をfalse扱いしない

IndexOfの戻り値0は、検索対象が先頭に見つかったことを意味します。

C#
string text = "C# tutorial";

int index = text.IndexOf("C#");

Console.WriteLine(index); // 0

0は「見つからない」ではありません。

見つからない場合だけ-1です。

10-2. -1との比較で判定する

IndexOfで見つかったかどうかを判定する場合は、-1と比較しましょう。

C#
if (text.IndexOf("C#") != -1)
{
Console.WriteLine("見つかりました");
}

または、次のように書くこともできます。

C#
if (text.IndexOf("C#") >= 0)
{
Console.WriteLine("見つかりました");
}

先頭に見つかった場合も含めて判定するため、>= 0は実務でもよく使われます。

避けたい書き方は次のようなコードです。

C#
if (text.IndexOf("C#") > 0)
{
Console.WriteLine("見つかりました");
}

この書き方では、先頭にある場合を見逃します。

10-3. 大文字・小文字を意識して比較する

文字列検索では、大文字・小文字を区別するかどうかを意識しましょう。

C#
string text = "CSharp";

Console.WriteLine(text.IndexOf("csharp")); // -1

大文字・小文字を無視したい場合は、StringComparison.OrdinalIgnoreCaseを指定します。

C#
string text = "CSharp";

int index = text.IndexOf("csharp", StringComparison.OrdinalIgnoreCase);

Console.WriteLine(index); // 0

ユーザー入力、検索キーワード、URL、拡張子などでは、大文字・小文字を無視した方が自然な場合があります。

10-4. StringComparisonを明示して意図をわかりやすくする

文字列検索では、StringComparisonを明示するとコードの意図がわかりやすくなります。

C#
int index = text.IndexOf(keyword, StringComparison.Ordinal);

大文字・小文字を無視するなら次のように書きます。

C#
int index = text.IndexOf(keyword, StringComparison.OrdinalIgnoreCase);

どの比較ルールを使うかが明確になるため、チーム開発でも読みやすいコードになります。

特に、実務では次のような書き方が推奨されます。

C#
bool found = text.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0;

10-5. 可読性を重視してContainsと使い分ける

位置が必要ない場合は、IndexOfよりContainsの方が読みやすいことがあります。

C#
if (text.Contains("C#"))
{
Console.WriteLine("C#が含まれています");
}

一方、位置を使って文字列を切り出す場合はIndexOfが必要です。

C#
int index = text.IndexOf(":");

if (index >= 0)
{
string value = text.Substring(index + 1);
Console.WriteLine(value);
}

使い分けの目安は次のとおりです。

目的使うメソッド
含まれているかだけ知りたいContains
位置を取得したいIndexOf
先頭かどうか知りたいStartsWith
末尾かどうか知りたいEndsWith

読みやすさを重視して、目的に合ったメソッドを選びましょう。

10-6. パフォーマンスが気になる場合の考え方

通常の文字列検索では、IndexOfのパフォーマンスを過度に心配する必要はありません。

ただし、大量の文字列を繰り返し検索する場合や、非常に長い文字列を扱う場合は注意が必要です。

たとえば、次のようなケースです。

C#
foreach (var line in lines)
{
if (line.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0)
{
Console.WriteLine(line);
}
}

このような処理が大量に実行される場合は、次の点を意識しましょう。

ToLowerToUpperで毎回新しい文字列を作るより、StringComparisonを使う方が無駄を減らせます。

C#
// 推奨
line.IndexOf(keyword, StringComparison.OrdinalIgnoreCase);

また、単純に含まれているかだけを調べるなら、Containsを使う方が意図が明確です。

C#
line.Contains(keyword, StringComparison.OrdinalIgnoreCase);

複雑なパターン検索が必要な場合はRegexを使いますが、単純な固定文字列検索であればIndexOfContainsの方がシンプルです。

11. C# IndexOfのよくある質問

ここでは、C#のIndexOfについてよくある質問に答えます。

11-1. IndexOfで複数回出現する文字列をすべて探せる?

IndexOfは、1回の呼び出しでは最初に見つかった位置だけを返します。

複数回出現する文字列をすべて探したい場合は、検索開始位置をずらしながら繰り返し検索します。

C#
string text = "abc abc abc";
string keyword = "abc";

int index = text.IndexOf(keyword);

while (index >= 0)
{
Console.WriteLine(index);
index = text.IndexOf(keyword, index + keyword.Length);
}

実行結果は次のとおりです。

C#
0
4
8

重なりを許して検索したい場合は、index + 1から次の検索を開始します。

C#
string text = "aaaa";
string keyword = "aa";

int index = text.IndexOf(keyword);

while (index >= 0)
{
Console.WriteLine(index);
index = text.IndexOf(keyword, index + 1);
}

実行結果は次のようになります。

C#
0
1
2

11-2. IndexOfで後ろから検索するには?

後ろから検索したい場合は、IndexOfではなくLastIndexOfを使います。

C#
string text = "abc abc abc";

int index = text.LastIndexOf("abc");

Console.WriteLine(index); // 8

IndexOfは前から検索し、LastIndexOfは後ろから検索します。

最後の区切り文字を探したい場合などに便利です。

C#
string path = "C:/work/sample.txt";

int index = path.LastIndexOf('/');

string fileName = path.Substring(index + 1);

Console.WriteLine(fileName); // sample.txt

11-3. IndexOfで日本語文字列も検索できる?

はい、日本語文字列も検索できます。

C#
string text = "C#の文字列検索を学ぶ";

int index = text.IndexOf("文字列");

Console.WriteLine(index); // 3

日本語の単語や文章の一部を検索することも可能です。

C#
string text = "今日は良い天気です";

if (text.IndexOf("天気") >= 0)
{
Console.WriteLine("天気という文字が含まれています");
}

ただし、全角・半角、ひらがな・カタカナ、異体字などは別の文字として扱われる場合があります。必要に応じて、検索前に文字列を正規化することも検討しましょう。

11-4. IndexOfで空文字を検索するとどうなる?

空文字を検索すると、通常は0が返ります。

C#
string text = "Hello";

int index = text.IndexOf("");

Console.WriteLine(index); // 0

空文字は文字列の先頭に存在するとみなされるためです。

検索開始位置を指定した場合は、その開始位置が返ることがあります。

C#
string text = "Hello";

int index = text.IndexOf("", 2);

Console.WriteLine(index); // 2

ただし、空文字を検索キーワードとして扱うと、意図しない結果になることがあります。

検索処理では、事前に空文字をチェックするのがおすすめです。

C#
if (!string.IsNullOrEmpty(keyword))
{
int index = text.IndexOf(keyword);
}

11-5. IndexOfとLINQはどちらを使うべき?

文字列の中から固定の文字や文字列を探すなら、基本的にはIndexOfを使うのが自然です。

C#
string text = "Hello C#";

int index = text.IndexOf("C#");

一方、配列やListに対して条件付きで検索したい場合は、LINQやFindIndexが便利です。

C#
List<string> names = new List<string>
{
"Tanaka",
"Sato",
"Suzuki"
};

int index = names.FindIndex(name => name.StartsWith("S"));
Console.WriteLine(index); // 1

LINQで要素を取得することもできます。

C#
string name = names.FirstOrDefault(name => name.StartsWith("S"));

Console.WriteLine(name); // Sato

位置が必要ならIndexOfFindIndex、要素そのものが欲しいならLINQを使うとよいでしょう。

まとめ

C#のIndexOfは、文字列や配列、Listの中から指定した値を検索し、その位置を取得するための基本的なメソッドです。

文字列で使う場合は、指定した文字や文字列が最初に見つかった位置を0から始まるインデックスで返します。見つからない場合は-1を返します。

C#
string text = "Hello C#";

Console.WriteLine(text.IndexOf("Hello")); // 0
Console.WriteLine(text.IndexOf("C#")); // 6
Console.WriteLine(text.IndexOf("Java")); // -1

特に重要なのは、戻り値0は「見つかった」という意味であり、「見つからない」ではないという点です。

検索結果を判定する場合は、次のように-1と比較しましょう。

C#
if (text.IndexOf("C#") >= 0)
{
Console.WriteLine("見つかりました");
}

大文字・小文字を区別せずに検索したい場合は、StringComparison.OrdinalIgnoreCaseを指定します。

C#
if (text.IndexOf("c#", StringComparison.OrdinalIgnoreCase) >= 0)
{
Console.WriteLine("見つかりました");
}

また、位置が必要ない場合はContains、後ろから検索したい場合はLastIndexOf、先頭一致はStartsWith、末尾一致はEndsWithを使うと、より読みやすいコードになります。

IndexOfはシンプルですが、文字列処理の基本となる重要なメソッドです。戻り値の意味、-1判定、大文字・小文字の扱い、StringComparisonの指定を正しく理解して、実務でも安全に使いこなしましょう。