ZetCode

C# search string

last modified November 6, 2026

In this article we show how to search for strings in C# language.

C# provides several methods for searching strings, including Contains, StartsWith, EndsWith, IndexOf, LastIndexOf, and IndexOfAny. More complex searches can be performed with the Regex class.

Most of these methods have overloads that take a StringComparison value. It determines whether the comparison is case-sensitive and whether it uses culture rules. StringComparison.Ordinal compares the characters' numeric values; it is fast and predictable. StringComparison.OrdinalIgnoreCase does the same while ignoring case.

C# Contains

The Contains method returns a boolean value indicating whether the substring occurs within the specified string.

Program.cs
var text = "An old hawk in the sky";
var word = "hawk";

if (text.Contains(word))
{
    Console.WriteLine($"The text contains the {word} word");
}
else
{
    Console.WriteLine($"The text does not contain the {word} word");
}

Console.WriteLine(text.Contains("Hawk"));
Console.WriteLine(text.Contains("Hawk", StringComparison.OrdinalIgnoreCase));
Console.WriteLine(text.Contains('k'));

We look for the hawk word in the sentence.

Console.WriteLine(text.Contains("Hawk"));
Console.WriteLine(text.Contains("Hawk", StringComparison.OrdinalIgnoreCase));

By default, Contains performs an ordinal, case-sensitive search. With StringComparison.OrdinalIgnoreCase, the case of the letters is ignored.

Console.WriteLine(text.Contains('k'));

There is also an overload that searches for a single character.

$ dotnet run
The text contains the hawk word
False
True
True

C# StartsWith and EndsWith

The StartsWith method determines whether the beginning of the string matches the specified string and the EndsWith method determines whether the end of the string matches the specified string.

Program.cs
var text = "old hawk";

if (text.StartsWith("old"))
{
    Console.WriteLine("The text starts with the old string");
}
else
{
    Console.WriteLine("The text does not start with the old string");
}

if (text.EndsWith("hawk"))
{
    Console.WriteLine("The text ends with the hawk string");
}
else
{
    Console.WriteLine("The text does not end with the hawk string");
}

string[] files = ["report.PDF", "notes.txt", "data.csv", "scan.pdf"];

foreach (var file in files)
{
    if (file.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
    {
        Console.WriteLine($"PDF file: {file}");
    }
}

We check the beginning and the end of a string. Then we look for PDF files in an array of file names.

if (file.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))

File extensions may come in any case. The case-insensitive comparison matches both report.PDF and scan.pdf.

$ dotnet run
The text starts with the old string
The text ends with the hawk string
PDF file: report.PDF
PDF file: scan.pdf

C# IndexOf and LastIndexOf

The IndexOf method finds the zero-based index of the first occurrence of a specified character or string. The LastIndexOf method finds the zero-based index of the last occurrence. Both methods return -1 if the character or string is not found.

Program.cs
var text = "There is an old hawk in the sky. The sun is shining.";
var word = "is";

int first = text.IndexOf(word, StringComparison.Ordinal);
int last = text.LastIndexOf(word, StringComparison.Ordinal);

Console.WriteLine($"The first index of the word {word}: {first}");
Console.WriteLine($"The last index of the word {word}: {last}");

int idx = text.IndexOf("eagle", StringComparison.Ordinal);
Console.WriteLine($"The index of the word eagle: {idx}");

We find the first and last occurrence of the is word.

int first = text.IndexOf(word, StringComparison.Ordinal);

The IndexOf(string) overload without a comparison argument uses the current culture. Passing StringComparison.Ordinal makes the search culture-independent; code analyzers recommend stating the comparison explicitly.

int idx = text.IndexOf("eagle", StringComparison.Ordinal);

The word eagle is not in the text, so the method returns -1.

$ dotnet run
The first index of the word is: 6
The last index of the word is: 41
The index of the word eagle: -1

C# find all occurrences

The IndexOf method has an overload with a starting position. We can use it to find all occurrences of a substring.

Program.cs
var text = "There is an old hawk in the sky. The sun is shining.";
var word = "is";

var indexes = new List<int>();
int pos = text.IndexOf(word, StringComparison.Ordinal);

while (pos != -1)
{
    indexes.Add(pos);
    pos = text.IndexOf(word, pos + word.Length, StringComparison.Ordinal);
}

Console.WriteLine($"The word {word} occurs {indexes.Count} times");
Console.WriteLine($"Indexes: {string.Join(", ", indexes)}");

We collect the indexes of all occurrences of the is word.

while (pos != -1)
{
    indexes.Add(pos);
    pos = text.IndexOf(word, pos + word.Length, StringComparison.Ordinal);
}

After each match, we continue the search right after the found substring. The loop ends when IndexOf returns -1.

$ dotnet run
The word is occurs 2 times
Indexes: 6, 41

C# IndexOfAny

The IndexOfAny and LastIndexOfAny methods search for the first and last occurrence of any character from a set.

Program.cs
using System.Buffers;

var text = "An old hawk in the sky";

char[] vowels = ['a', 'e', 'i', 'o', 'u'];

int first = text.IndexOfAny(vowels);
int last = text.LastIndexOfAny(vowels);

Console.WriteLine($"First vowel at {first}: {text[first]}");
Console.WriteLine($"Last vowel at {last}: {text[last]}");

SearchValues<char> digits = SearchValues.Create("0123456789");

var code = "item-AB-2024-x";
int d = code.AsSpan().IndexOfAny(digits);

Console.WriteLine($"First digit at {d}: {code[d]}");

We find the first and the last vowel in the text and the first digit in a product code.

char[] vowels = ['a', 'e', 'i', 'o', 'u'];

int first = text.IndexOfAny(vowels);
int last = text.LastIndexOfAny(vowels);

The set of characters is passed as an array. The capital A at the beginning is not in the set, so the first vowel is the o of old.

SearchValues<char> digits = SearchValues.Create("0123456789");

The SearchValues<char> type, available since .NET 8, holds a precomputed set of values. When the same set is searched many times, it is faster than an array.

int d = code.AsSpan().IndexOfAny(digits);

The IndexOfAny overload taking SearchValues is defined on spans, so we call AsSpan first.

$ dotnet run
First vowel at 3: o
Last vowel at 17: e
First digit at 8: 2

C# Regex Match

The Regex class can be used for more complex searches. The C# Regex tutorial covers regular expressions in C# in more detail.

The Success property of a Match indicates whether the match is successful. The NextMatch method returns a new Match object with the results for the next match, starting at the position at which the last match ended. The position of a match is available in the Index property.

Program.cs
using System.Text.RegularExpressions;

var content = """
    Foxes are omnivorous mammals belonging to several genera
    of the family Canidae. Foxes have a flattened skull, upright triangular ears,
    a pointed, slightly upturned snout, and a long bushy tail. Foxes live on every
    continent except Antarctica. By far the most common and widespread species of
    fox is the red fox.
    """;

var rx = new Regex("fox(es)?", RegexOptions.IgnoreCase);

Match match = rx.Match(content);

while (match.Success)
{
    Console.WriteLine($"{match.Value} at index {match.Index}");
    match = match.NextMatch();
}

Console.WriteLine($"Total matches: {rx.Count(content)}");

We look for all occurrences of the fox word.

var rx = new Regex("fox(es)?", RegexOptions.IgnoreCase);

We add the (es)? expression to include the plural form of the word. The RegexOptions.IgnoreCase option searches in case-insensitive mode.

Match match = rx.Match(content);

while (match.Success)
{
    Console.WriteLine($"{match.Value} at index {match.Index}");
    match = match.NextMatch();
}

The Value property returns the matched string and the Index property returns its position in the text. The NextMatch method finds the next occurrence.

Console.WriteLine($"Total matches: {rx.Count(content)}");

The Count method, available since .NET 7, returns the number of matches without creating Match objects.

$ dotnet run
Foxes at index 0
Foxes at index 80
Foxes at index 194
fox at index 292
fox at index 307
Total matches: 5

C# GeneratedRegex

The GeneratedRegex attribute makes the compiler generate the regular expression code at build time. This avoids the cost of parsing and compiling the pattern at run time.

Program.cs
using System.Text.RegularExpressions;

var text = "A fox, two foxes, a foxhole, and the Firefox browser.";

foreach (Match m in Patterns.Fox().Matches(text))
{
    Console.WriteLine($"{m.Value} at index {m.Index}");
}

static partial class Patterns
{
    [GeneratedRegex(@"\bfox(es)?\b", RegexOptions.IgnoreCase)]
    public static partial Regex Fox();
}

We search for the whole words fox and foxes.

[GeneratedRegex(@"\bfox(es)?\b", RegexOptions.IgnoreCase)]
public static partial Regex Fox();

The attribute is applied to a partial method; the source generator provides its body. The \b anchors denote word boundaries, so foxhole and Firefox are not matched.

$ dotnet run
fox at index 2
foxes at index 11

Source

String class - language reference

In this article we have searched for strings in C# language.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.