Nota
L-aċċess għal din il-paġna jeħtieġ l-awtorizzazzjoni. Tista’ tipprova tidħol jew tibdel id-direttorji.
L-aċċess għal din il-paġna jeħtieġ l-awtorizzazzjoni. Tista’ tipprova tibdel id-direttorji.
This article covers three string operations: regular-expression pattern matching with System.Text.RegularExpressions.Regex, allocation-free search over ReadOnlySpan<T>, and choosing a StringComparison value for correct, fast comparisons.
Find specific text by using regular expressions
The System.Text.RegularExpressions.Regex class searches strings for patterns rather than fixed substrings. The static Regex.IsMatch method takes the input string, a pattern, and optional RegexOptions flags.
The following example searches each sentence for the word the or their, case insensitive. The pattern the(ir)?\s matches the optionally followed by ir, then a whitespace character:
| Pattern | Meaning |
|---|---|
the |
match the literal text the |
(ir)? |
match 0 or 1 occurrence of ir |
\s |
match a whitespace character |
string[] sentences =
[
"Put the water over there.",
"They're quite thirsty.",
"Their water bottles broke."
];
string pattern = @"the(ir)?\s";
foreach (string s in sentences)
{
Console.Write($"{s,28}");
if (Regex.IsMatch(s, pattern, RegexOptions.IgnoreCase))
{
Console.WriteLine($" (match for '{pattern}' found)");
}
else
{
Console.WriteLine();
}
}
Validate strings against a pattern
To check whether an entire input matches a shape, anchor the pattern with ^ and $. The following example validates that each string is a US-style telephone number: three digits, three digits, four digits, separated by dashes:
| Pattern | Meaning |
|---|---|
^ |
match the beginning of the string |
\d{3} |
match exactly three digit characters |
- |
match a literal - character |
\d{4} |
match exactly four digit characters |
$ |
match the end of the string |
string[] numbers =
[
"123-555-0190",
"444-234-22450",
"690-555-0178",
"146-893-232",
"146-555-0122",
"4007-555-0111",
"407-555-0111",
"407-2-5555",
"407-555-8974",
"407-2ab-5555",
"690-555-8148",
"146-893-232-"
];
string pattern = """^\d{3}-\d{3}-\d{4}$""";
foreach (string s in numbers)
{
Console.Write($"{s,14}");
Console.WriteLine(Regex.IsMatch(s, pattern) ? " - valid" : " - invalid");
}
For the full pattern syntax, see Regular expression language - quick reference.
Choose between string methods and regular expressions
string methods and Regex solve overlapping problems. Prefer string methods when the text you're searching for is a literal value, a known prefix or suffix, or a fixed delimiter. They're simpler to read and faster, because they don't pay the cost of compiling and executing a pattern. Reach for Regex when the search target is a shape, such as alternations, optional groups, repeated character classes, or anchored validation. As a rule of thumb, if you can write the search as one or two string.Contains / StartsWith / IndexOf calls, do so.
Search using ReadOnlySpan<char>
When you parse large inputs or run a search on a hot path, the per-call allocations of string.Substring and string.Split can dominate. ReadOnlySpan<char> gives you a view over an existing string (or array, or stack buffer) without copying, and MemoryExtensions provides span-based equivalents of the common string methods, including IndexOf:
ReadOnlySpan<char> input = "key1=alpha;key2=beta;key3=gamma".AsSpan();
ReadOnlySpan<char> needle = "key2=".AsSpan();
int start = input.IndexOf(needle);
if (start >= 0)
{
ReadOnlySpan<char> rest = input[(start + needle.Length)..];
int end = rest.IndexOf(';');
ReadOnlySpan<char> value = end >= 0 ? rest[..end] : rest;
Console.WriteLine($"key2 = {value}");
}
// => key2 = beta
Span-based search avoids allocations because the slices (input[start..], rest[..end]) are simply windows over the original characters. The same approach scales to parsing key-value lists, headers, and other delimited text without ever calling Substring.
Performance considerations for StringComparison
Most string instance methods have overloads that accept a StringComparison value. Methods such as String.Equals(String) default to ordinal, but String.Compare(String, String) and String.IndexOf(String) default to current culture. This difference matters in two ways:
- Speed. Ordinal comparison is a byte-for-byte test that runs in tight, vectorized loops. Culture-aware comparison consults a sort table, walks combining characters, and applies locale-specific rules. For the same input, it can be an order of magnitude slower.
- Correctness. Culture-aware comparison can fold characters that you don't expect (Turkish
i/I, Germanßtoss, ligatures). This behavior is right for sorting names a user sees but wrong for parsing identifiers, paths, or protocol tokens.
For machine-defined text, such as file names, URLs, HTTP headers, identifiers, and configuration keys, pass StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase explicitly. Reserve culture-aware values for natural-language text shown to users. For comprehensive guidance, see Best practices for comparing strings in .NET.