String Predicates and Matching
These methods answer yes/no questions about a string: does it contain, start with or end with one of several values, is it blank, is it Base64, does it match a *.csv pattern. A few also measure a string: how often a value occurs, and what two strings share at the start or end. Reach for them when you would otherwise chain || calls or guard against null before every check.
All methods live in the System namespace, so you do not need a using directive.
At a glance
| Method | What it does |
|---|---|
Contains(params string[]) | true if the string contains any of the values (ordinal, case-sensitive). |
ContainsAny | true if the string contains any of the values, using the StringComparison you pass. |
ContainsIgnoreCase | true if the string contains the value, or any of the values, ignoring case. |
StartsWith(params string[]) | true if the string starts with any of the prefixes (ordinal, case-sensitive). |
StartsWithIgnoreCase | true if the string starts with the prefix, or any of the prefixes, ignoring case. |
EndsWith(params string[]) | true if the string ends with any of the suffixes (ordinal, case-sensitive). |
EndsWithIgnoreCase | true if the string ends with the suffix, or any of the suffixes, ignoring case. |
EqualsIgnoreCase | Compares two strings ignoring case. Two null strings are equal. |
IsNullOrEmpty | Extension form of string.IsNullOrEmpty, with nullable flow analysis. |
IsNullOrWhiteSpace | Extension form of string.IsNullOrWhiteSpace, with nullable flow analysis. |
IsBase64 | true if the string is valid standard Base64 with no whitespace. |
MatchesWildcard | Matches the whole string against a glob pattern with * and ?. |
CountOccurrences | Counts non-overlapping occurrences of a string, or occurrences of a character. |
CommonPrefix | Returns the longest start that two strings share. |
CommonSuffix | Returns the longest end that two strings share. |
IsAnagramOf | true if two strings use the same letters and digits, ignoring case, spaces and punctuation. |
The null-receiver rule
The string you call a method on (the receiver) may be null. The values you pass in may not.
- A
nullreceiver gives a "no" answer: predicates returnfalse,CountOccurrencesreturns0. NoNullReferenceException. - A
nullargument (the value to look for, the pattern, the other string) throwsArgumentNullExceptionwith the parameter name. Arguments are checked first, so this happens even when the receiver is alsonull. - Every comparison is ordinal unless you pass a
StringComparison. Nothing depends on the current culture.
string? couponCode = null;
bool hasPrefix = couponCode.StartsWithIgnoreCase("SPRING"); // → false
int dashes = couponCode.CountOccurrences('-'); // → 0
bool blank = couponCode.IsNullOrWhiteSpace(); // → true
string? prefix = null;
couponCode.StartsWithIgnoreCase(prefix!); // throws ArgumentNullException (ParamName "toCheck")
| Method | Result for a null receiver |
|---|---|
Contains, ContainsAny, ContainsIgnoreCase | false |
StartsWith, StartsWithIgnoreCase, EndsWith, EndsWithIgnoreCase | false |
EqualsIgnoreCase | true if the other string is also null, otherwise false |
IsNullOrEmpty, IsNullOrWhiteSpace | true |
IsBase64, MatchesWildcard, IsAnagramOf | false |
CountOccurrences | 0 |
CommonPrefix, CommonSuffix | throws ArgumentNullException (these return a string, so there is no safe answer) |
See Conventions for the rules that apply across the whole library.
Contains, StartsWith and EndsWith
Each family has a case-sensitive form that takes several values and an IgnoreCase form that takes one or several values. They return true as soon as one value matches. An empty list of values returns false.
string already has instance methods Contains(string), StartsWith(string) and EndsWith(string). C# prefers instance methods, so a call with one string argument, such as url.StartsWith("https://"), runs the BCL method, not this library. That method throws NullReferenceException on a null string, and the BCL StartsWith(string) / EndsWith(string) compare with the current culture.
The extensions take over when you pass two or more values, or a string[]. For a single value, use the IgnoreCase method, ContainsAny(comparison, value), or pass a one-element array.
Contains
Contains(params string[] toCheck) checks whether the string contains any of the values. The comparison is ordinal and case-sensitive.
string logLine = "2024-05-01 12:00:03 ERROR Payment gateway timeout";
bool isProblem = logLine.Contains("ERROR", "FATAL"); // → true
bool lowerCase = logLine.Contains("error", "fatal"); // → false
string[] blockedWords = { "casino", "lottery" };
string comment = "Win the lottery today!";
bool isSpam = comment.Contains(blockedWords); // → true
- An empty string is contained in every string, so
"abc".Contains("x", "")returnstrue. - A
nullarray or anullitem in it throwsArgumentNullException(ParamNameis"toCheck").
ContainsAny
ContainsAny(StringComparison comparison, params string[] toCheck) is the same check with a comparison you choose. Use it when you need case-insensitive matching against a list built at run time, or a culture-aware comparison.
string productName = "Wireless Mouse M185";
bool isPeripheral = productName.ContainsAny(StringComparison.OrdinalIgnoreCase, "mouse", "keyboard"); // → true
bool exactCase = productName.ContainsAny(StringComparison.Ordinal, "mouse", "keyboard"); // → false
The comparison is passed straight to string.IndexOf, so CurrentCulture and InvariantCultureIgnoreCase work too.
ContainsIgnoreCase
ContainsIgnoreCase(string toCheck) and ContainsIgnoreCase(params string[] toCheck) check for one value, or any of several values, using StringComparison.OrdinalIgnoreCase.
string subject = "Re: Invoice INV-2041 is OVERDUE";
bool aboutInvoice = subject.ContainsIgnoreCase("invoice"); // → true
bool escalate = subject.ContainsIgnoreCase("urgent", "overdue"); // → true
bool aboutRefund = subject.ContainsIgnoreCase("refund"); // → false
string? emptySubject = null;
bool fromNull = emptySubject.ContainsIgnoreCase("invoice"); // → false
ContainsIgnoreCase(StringComparison comp, string toCheck) and ContainsIgnoreCase(StringComparison comp, params string[] toCheck) still compile but are marked [Obsolete]: the comparison argument decides the case rule, so the name "IgnoreCase" is misleading. Replace them with ContainsAny.
string customerNote = "Gold member since 2019";
// Before: compiler warning CS0618
bool oldStyle = customerNote.ContainsIgnoreCase(StringComparison.Ordinal, "VIP", "Gold"); // → true
// After
bool newStyle = customerNote.ContainsAny(StringComparison.Ordinal, "VIP", "Gold"); // → true
StartsWith
StartsWith(params string[] toCheck) checks whether the string starts with any of the prefixes. Ordinal and case-sensitive.
string callbackUrl = "https://shop.example.com/orders/1042";
bool isWebUrl = callbackUrl.StartsWith("http://", "https://"); // → true
bool isFtp = callbackUrl.StartsWith("ftp://", "sftp://"); // → false
bool upperCase = callbackUrl.StartsWith("HTTP://", "HTTPS://"); // → false
StartsWithIgnoreCase
StartsWithIgnoreCase(string toCheck) and StartsWithIgnoreCase(params string[] toCheck) check one prefix, or any of several, with StringComparison.OrdinalIgnoreCase.
string authorization = "bearer eyJhbGciOiJIUzI1NiJ9";
bool isBearer = authorization.StartsWithIgnoreCase("Bearer "); // → true
bool isSupported = authorization.StartsWithIgnoreCase("Basic ", "Bearer "); // → true
bool isDigest = authorization.StartsWithIgnoreCase("Digest "); // → false
EndsWith
EndsWith(params string[] toCheck) checks whether the string ends with any of the suffixes. Ordinal and case-sensitive.
string exportFile = "sales-2024-q3.csv";
bool isTabular = exportFile.EndsWith(".csv", ".xlsx"); // → true
bool isImage = exportFile.EndsWith(".png", ".jpg"); // → false
EndsWithIgnoreCase
EndsWithIgnoreCase(string toCheck) and EndsWithIgnoreCase(params string[] toCheck) check one suffix, or any of several, with StringComparison.OrdinalIgnoreCase. File extensions are the usual case.
string upload = "receipt-2041.JPG";
bool exactCase = upload.EndsWith(".jpg", ".png"); // → false
bool isImage = upload.EndsWithIgnoreCase(".jpg", ".png"); // → true
bool isPdf = upload.EndsWithIgnoreCase(".pdf"); // → false
Equality and blank checks
EqualsIgnoreCase
EqualsIgnoreCase(string? toCheck) compares two strings with StringComparison.OrdinalIgnoreCase. Both sides may be null; two null strings count as equal.
string? enteredEmail = "Ana.Lopez@Example.com";
string storedEmail = "ana.lopez@example.com";
bool sameUser = enteredEmail.EqualsIgnoreCase(storedEmail); // → true
string? missing = null;
bool bothMissing = missing.EqualsIgnoreCase(null); // → true
bool oneMissing = missing.EqualsIgnoreCase("ana"); // → false
In 1.x, EqualsIgnoreCase returned false for two null strings. It now returns true, the same as string.Equals. See Upgrading from 1.x.
IsNullOrEmpty
IsNullOrEmpty() is string.IsNullOrEmpty as an extension, so it reads left to right and works on a null receiver. The receiver carries [NotNullWhen(false)]: after a false result the compiler knows the string is not null.
string? orderNote = "";
bool noNote = orderNote.IsNullOrEmpty(); // → true
bool space = " ".IsNullOrEmpty(); // → false
bool text = "a".IsNullOrEmpty(); // → false
IsNullOrWhiteSpace
IsNullOrWhiteSpace() is string.IsNullOrWhiteSpace as an extension, also with [NotNullWhen(false)]. Use it to treat blank form input as missing.
static string DisplayName(string? nickname, string fullName)
{
if (!nickname.IsNullOrWhiteSpace())
{
// No CS8602 warning: the compiler knows nickname is not null here.
return nickname.Trim();
}
return fullName;
}
string blankNick = DisplayName(" \t\n", "Ana Lopez"); // → "Ana Lopez"
string realNick = DisplayName(" Ana ", "Ana Lopez"); // → "Ana"
Format checks
IsBase64
IsBase64() returns true when the string decodes as standard Base64. It is stricter than Convert.FromBase64String: the length must be a multiple of 4 and the string may not contain any whitespace.
bool padded = "SGVsbG8=".IsBase64(); // → true
bool full = "SGVsbG8h".IsBase64(); // → true
bool tooShort = "SGVsbG8".IsBase64(); // → false (length is not a multiple of 4)
bool spaced = "SGVs bG8".IsBase64(); // → false (contains whitespace)
bool symbols = "!!!!".IsBase64(); // → false
bool empty = "".IsBase64(); // → false
Only the standard alphabet (A–Z, a–z, 0–9, +, /, with = padding) is accepted. URL-safe Base64, which uses - and _, returns false. The check decodes into a temporary buffer, so it allocates about three quarters of the input length.
For encoding and decoding helpers, see Encoding and validation.
Pattern matching and counting
MatchesWildcard
MatchesWildcard(string pattern) and MatchesWildcard(string pattern, bool ignoreCase) test the whole string against a glob pattern. * matches any run of characters, including none. ? matches exactly one character. Every other character matches itself. Without ignoreCase the match is ordinal and case-sensitive.
bool monthly = "report-2024.csv".MatchesWildcard("report-*.csv"); // → true
bool noDash = "report.csv".MatchesWildcard("report-*.csv"); // → false
bool emptyRun = "report-.csv".MatchesWildcard("report-*.csv"); // → true
bool oneChar = "abc".MatchesWildcard("a?c"); // → true
bool noChar = "ac".MatchesWildcard("a?c"); // → false
bool exactCase = "Report.CSV".MatchesWildcard("*.csv"); // → false
bool anyCase = "Report.CSV".MatchesWildcard("*.csv", ignoreCase: true); // → true
string[] uploads = { "orders.csv", "logo.png", "Refunds.CSV", "notes.txt" };
List<string> csvFiles = uploads
.Where(name => name.MatchesWildcard("*.csv", ignoreCase: true))
.ToList(); // → ["orders.csv", "Refunds.CSV"]
- The pattern must cover the entire string.
"a".MatchesWildcard("")isfalse;"".MatchesWildcard("*")and"".MatchesWildcard("")aretrue;"".MatchesWildcard("?")isfalse. - There is no escape character and no
[abc]character class. A*or?in the pattern is always a wildcard. ignoreCase: truecompares characters withchar.ToUpperInvariant, so it does not depend on the current culture.?matches one UTF-16char. A character outside the Basic Multilingual Plane, such as most emoji, is twochars.- A
nulltext returnsfalse; anullpattern throwsArgumentNullException(ParamNameis"pattern").
The matcher is iterative, with no regex and no recursion. A hostile pattern such as fifty * followed by b, run against 10,000 a characters, finishes quickly and returns false. You can accept patterns from users without adding a timeout.
CountOccurrences
CountOccurrences has three overloads:
CountOccurrences(string value)counts non-overlapping occurrences, ordinal and case-sensitive.CountOccurrences(string value, StringComparison comparison)does the same with the comparison you choose.CountOccurrences(char value)counts a single character.
Matches do not overlap: after a match, the search continues after the end of that match.
int ana = "banana".CountOccurrences("ana"); // → 1
int letterA = "banana".CountOccurrences("a"); // → 3
int pairs = "aaaa".CountOccurrences("aa"); // → 2
int anyCase = "Aa aA".CountOccurrences("aa", StringComparison.OrdinalIgnoreCase); // → 2
int commas = "a,b,c".CountOccurrences(','); // → 2
string csvLine = "1042,Ana Lopez,2024-05-01,149.90";
int columns = csvLine.CountOccurrences(',') + 1; // → 4
- A
nulltext returns0for every overload. - A
nullvalue throwsArgumentNullException; an empty value throwsArgumentException. Both reportParamName"value". The value is checked before the text, so an empty value throws even when the text isnull.
Comparing two strings
CommonPrefix
CommonPrefix(string other) returns the longest start that both strings share, compared ordinally (case-sensitive). The result may be empty.
string firstRoute = "/api/orders/1042";
string secondRoute = "/api/orders/2077";
string sharedRoute = firstRoute.CommonPrefix(secondRoute); // → "/api/orders/"
string word = "interstellar".CommonPrefix("internet"); // → "inter"
string shorter = "ab".CommonPrefix("abc"); // → "ab"
string none = "abc".CommonPrefix("xyz"); // → ""
string caseMatters = "Order".CommonPrefix("order"); // → ""
CommonSuffix
CommonSuffix(string other) returns the longest end that both strings share, compared ordinally (case-sensitive). The result may be empty.
string invoiceFile = "invoice-2041.pdf";
string receiptFile = "receipt-2041.pdf";
string sharedEnd = invoiceFile.CommonSuffix(receiptFile); // → "-2041.pdf"
string word = "walking".CommonSuffix("talking"); // → "alking"
string none = "abc".CommonSuffix("xyz"); // → ""
Unlike the predicates on this page, CommonPrefix and CommonSuffix take a non-nullable receiver. A null receiver or a null other throws ArgumentNullException (ParamName is "text" or "other").
IsAnagramOf
IsAnagramOf(string other) returns true when both strings contain the same letters and digits, in any order. It ignores case, whitespace and punctuation. Letters are lower-cased with the invariant culture.
bool room = "Dormitory".IsAnagramOf("dirty room"); // → true
bool listen = "listen".IsAnagramOf("silent"); // → true
bool gentleman = "A gentleman".IsAnagramOf("Elegant man!"); // → true
bool different = "abc".IsAnagramOf("abd"); // → false
bool extraLetter = "abc".IsAnagramOf("abcc"); // → false
- Two strings without any letter or digit are anagrams:
"!!!".IsAnagramOf("???")and"".IsAnagramOf("")are bothtrue. - A
nullreceiver returnsfalse; anullotherthrowsArgumentNullException(ParamNameis"other").
For similarity scores and edit distances rather than exact matches, see Fuzzy and natural matching.
See also
- Conventions: null handling, ordinal comparisons and exceptions across the library.
- String manipulation: trimming, truncating and ensuring prefixes and suffixes.
- Fuzzy and natural matching: edit distance, similarity and natural sort.
- Encoding and validation: Base64 and other encoding helpers.