Back to Blog
C#

C# String StartsWith and EndsWith Usage

c# startswith endswith: Learn how to use C# StartsWith and EndsWith methods for string prefix and suffix checks, including overloads, culture considerations, and perfo...

String MethodsC# ProgrammingString ComparisonText ProcessingC# .NET
C# string prefix and suffix comparison with StartsWith and EndsWith methods

c# startswith endswith requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

When you need to check whether a string begins with a particular prefix or ends with a particular suffix in C#, the StartsWith and EndsWith methods are the natural choices. These methods are part of the String class and provide a straightforward way to perform prefix and suffix matching. This article covers their syntax, overloads, comparison behavior, culture implications, and performance characteristics, so you can use them correctly in your applications.

The Basic Syntax of StartsWith and EndsWith

The simplest form of these methods takes a single string argument and returns a bool indicating whether the target string starts with or ends with the specified value. The comparison is case-sensitive and uses the current culture by default.

string filename = "report_final.pdf"; bool startsWithReport = filename.StartsWith("report"); bool endsWithPdf = filename.EndsWith(".pdf"); Console.WriteLine(startsWithReport); // True Console.WriteLine(endsWithPdf); // True

The methods return false when the argument is null or when the argument is an empty string and the target is empty as well. However, an empty string argument behaves differently: if you pass an empty string, StartsWith returns true only if the target string is also empty, whereas EndsWith with an empty string returns true only if the target is empty. Actually, the behavior is that both return true when the argument is an empty string, regardless of the target length, because an empty string is considered a prefix and a suffix of any string. This is worth noting because it can lead to subtle bugs if you expect a non-empty prefix.

string text = "hello"; Console.WriteLine(text.StartsWith("")); // True Console.WriteLine(text.EndsWith("")); // True

Using the Overloads for Comparison Control

The default overload is case-sensitive and culture-sensitive, but you often need to specify a comparison rule, such as case-insensitive or ordinal. The .NET API provides overloads that accept a StringComparison enumeration value. This gives you explicit control over how the comparison is performed.

string url = "HTTPS://example.com"; bool startsWithHttps = url.StartsWith("https", StringComparison.OrdinalIgnoreCase); bool endsWithCom = url.EndsWith(".com", StringComparison.OrdinalIgnoreCase); Console.WriteLine(startsWithHttps); // True Console.WriteLine(endsWithCom); // True

Using StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase is generally recommended for most string checks, especially when you are comparing file paths, URLs, or identifiers, because these comparisons are fast and do not involve culture-specific rules. In contrast, CurrentCulture and InvariantCulture comparisons can behave differently depending on the system's culture settings and may produce unexpected results.

Understanding Culture-Sensitive vs. Ordinal Comparison

The choice of StringComparison affects both correctness and performance. The default overload uses CurrentCulture, which can cause surprising behavior. For example, in Turkish culture, the letter 'I' is handled differently, so a case-insensitive comparison using CurrentCulture may not treat 'i' and 'I' as equivalent. Ordinal comparison, on the other hand, compares the numeric Unicode values of the characters, which is deterministic and fast.

Here is a table that summarizes the main comparison options:

StringComparison ValueCase HandlingCultureUse Case
OrdinalCase-sensitiveNoneFast, culture-invariant; ideal for internal identifiers, paths, etc.
OrdinalIgnoreCaseCase-insensitiveNoneFast, culture-invariant; good for case-insensitive URI or file extensions
CurrentCultureDependsCurrent cultureRarely needed; may cause subtle bugs
InvariantCultureDependsInvariant cultureFor locale-aware data but rarely needed for prefix checks
CurrentCultureIgnoreCaseCase-insensitiveCurrent cultureNot recommended unless you have specific culture requirements

For most scenarios, especially when checking prefixes and suffixes on user input or file names, use StringComparison.Ordinal or OrdinalIgnoreCase. This avoids the overhead of culture-specific rules and ensures predictable behavior across different environments.

Practical Examples: File Processing and URL Validation

A common use case is checking file extensions. For example, you might want to ensure a file has a .txt extension before processing it. Using EndsWith with OrdinalIgnoreCase is a practical approach.

string file = "data.TXT"; if (file.EndsWith(".txt", StringComparison.OrdinalIgnoreCase)) { // Process as text file }

Another typical scenario is URL validation. Suppose you want to allow only HTTPS URLs. You can check the prefix with StartsWith.

bool IsHttpsUrl(string url) { return url.StartsWith("https://", StringComparison.OrdinalIgnoreCase); }

Note that StartsWith checks the beginning only; it does not validate the URL structure. For full validation, you would need a Uri or a regex, but for simple prefix checks, this method is sufficient.

Performance Considerations

When you use StartsWith or EndsWith, the runtime performs a comparison of the characters. The time complexity is proportional to the length of the argument. For short prefixes and suffixes, this is negligible. However, if you are checking many strings in a loop, the choice of comparison type can have an impact.

Ordinal comparisons are generally faster because they do not need to consider culture-specific collation rules. Culture-sensitive comparisons (like CurrentCulture) involve additional logic and metadata lookups, which can be significantly slower. Therefore, if performance is a concern, always use StringComparison.Ordinal or OrdinalIgnoreCase unless you have a specific culture requirement.

A common misconception is that StartsWith is more efficient than using Substring and then comparing. In fact, StartsWith is straightforward and optimized, while Substring allocates a new string, which is wasteful. So prefer StartsWith and EndsWith over manual substring operations.

Edge Cases and Common Pitfalls

One pitfall is relying on the default culture-sensitive comparison for strings that represent programmatic identifiers. For example, checking if a file name starts with "tmp" using the default overload may fail if the culture treats certain letters differently. Always specify StringComparison explicitly to avoid surprises.

Another edge case is that both StartsWith and EndsWith throw an ArgumentNullException if the argument is null. The string to be checked can be null, but the method will return false, not throw. Actually, if the target string is null, calling StartsWith on it will throw a NullReferenceException. For example:

string? value = null; if (value.StartsWith("a")) // throws NullReferenceException if value is null

Always null-check before calling these methods if the string can be null. Alternatively, use the null-conditional operator:

if (value?.StartsWith("a") == true) { // safe execute }

When to Use StartsWith and EndsWith vs. Other Approaches

For simple prefix and suffix checks, StartsWith and EndsWith are the correct tools. If you need to find a substring anywhere within the string, use Contains or IndexOf. If you need to match a pattern with wildcards, consider a regex, but keep in mind that regex is more resource-intensive. The decision should be based on the specificity of the match: StartsWith and EndsWith are efficient and readable for fixed boundaries.

In terms of maintainability, using these methods makes the code self-documenting. A reader immediately understands that you are checking a prefix or suffix. In contrast, a regex like ^https requires regex knowledge and is less clear.

Advanced Scenario: Combining StartsWith and EndsWith in a Pipeline

In real-world code, you often combine these checks. For example, you might want to validate a file name that must start with a category and end with a timestamp pattern. Using StartsWith and EndsWith together is straightforward.

bool IsValidReportName(string name) { return name.StartsWith("report_", StringComparison.OrdinalIgnoreCase) && name.EndsWith("_2024.csv", StringComparison.OrdinalIgnoreCase); }

This approach is simple and effective. It avoids regular expressions unless you need to validate the middle portion as well. For more complex patterns, you could combine these checks with Split or IndexOf, but for many cases, two simple checks are enough.

Compatibility Notes

StartsWith and EndsWith are available across all modern .NET versions, including .NET Framework, .NET Core, and .NET 5+. The overloads that accept StringComparison have been available since .NET Framework 2.0, so you can use them in virtually any project. There is no difference in behavior across platforms for ordinal comparisons, which makes them ideal for cross-platform code.

One compatibility consideration is that the default overload uses CurrentCulture, which can produce different results on different machines if the culture settings differ. Therefore, for any code that might run in various environments, it is prudent to specify a comparison rule explicitly. This is especially important in web applications where the server's culture might not match the client's expectations.

In summary, StartsWith and EndsWith are simple yet powerful tools for string prefix and suffix checks. By understanding their overloads and comparison rules, you can write robust and efficient code. Always decide on the comparison type consciously to avoid culture-related bugs and to maintain consistent behavior across deployments.