Question
How can I count how many times a character or substring appears inside a string in C#?
For example, I want to count how many / characters exist in a string. I know there are several ways to do this, but I am not sure which approach is the best or simplest.
One approach I am currently using is:
string source = "/once/upon/a/time/";
int count = source.Length - source.Replace("/", "").Length;
For substrings with length greater than 1, I am using:
string haystack = "/once/upon/a/time";
string needle = "/";
int needleCount = (haystack.Length - haystack.Replace(needle, "").Length) / needle.Length;
Is this a good way to count occurrences, or is there a better or more readable approach?
Short Answer
By the end of this page, you will understand how to count the number of times a character or substring appears in a string in C#. You will learn simple approaches using Replace, loops, LINQ, and substring searching, plus when each option is appropriate.
Concept
In C#, counting occurrences in a string means checking how many times a specific value appears inside a larger piece of text.
This value can be:
- a single character, such as
'/' - a substring, such as
"cat"
This matters because string processing is common in real programs. Developers often need to:
- count separators in file paths or URLs
- count words or tokens in input text
- validate data formats
- inspect logs or API responses
- parse structured text
A string in C# is immutable, which means methods like Replace do not change the original string. Instead, they create a new string. That is why this technique works:
int count = source.Length - source.Replace("/", "").Length;
The code removes all / characters, compares the old and new lengths, and uses the difference as the count.
This is clever and short, but it is not always the clearest or most efficient option, especially for longer strings or repeated operations. For single-character counting, a loop or LINQ is often easier to read. For substring counting, searching with IndexOf in a loop is usually a better general solution.
The main idea is:
- Character counting: inspect each character and compare it
- Substring counting: search for the target repeatedly until no more matches are found
Mental Model
Imagine a string as a long row of tiles.
- If you want to count a character, you walk across the tiles one by one and count every tile that matches.
- If you want to count a substring, you slide a small pattern across the row and check where it fits.
Another way to think about Replace is this:
- Start with a rope of a certain length
- Cut out every
/ - Measure the rope again
- The missing length tells you how many characters were removed
That works well for simple counting, but if you want more control, walking through the string directly is often easier to understand.
Syntax and Examples
1. Count a single character with a loop
string source = "/once/upon/a/time/";
int count = 0;
foreach (char c in source)
{
if (c == '/')
{
count++;
}
}
Console.WriteLine(count); // 4
This is beginner-friendly and very readable:
- loop through each character
- compare it to
'/' - increase the counter when it matches
2. Count a single character with LINQ
using System.Linq;
string source = "/once/upon/a/time/";
int count = source.Count(c => c == '/');
Console.WriteLine(count); // 4
This is short and expressive. It works well when you already use LINQ.
3. Count a substring with Replace
string haystack = "abc--def--ghi--";
string needle = "--";
int count = (haystack.Length - haystack.Replace(needle, "").Length) / needle.Length;
Console.WriteLine(count); // 3
This works because each occurrence of needle removes needle.Length characters.
4. Count a substring with IndexOf
string haystack = "abc--def--ghi--";
string needle = "--";
int count = 0;
int index = 0;
while ((index = haystack.IndexOf(needle, index)) != -1)
{
count++;
index += needle.Length;
}
Console.WriteLine(count); // 3
This is a strong general-purpose solution for substring counting.
5. Count overlapping substrings
string haystack = "aaaa";
string needle = "aa";
int count = 0;
int index = 0;
while ((index = haystack.IndexOf(needle, index)) != -1)
{
count++;
index += 1;
}
Console.WriteLine(count); // 3
The matches are:
- positions 0 to 1
- positions 1 to 2
- positions 2 to 3
If you move forward by needle.Length, you count only non-overlapping matches. If you move forward by 1, you allow overlapping matches.
Step by Step Execution
Consider this example:
string text = "/a/b/c/";
int count = 0;
foreach (char c in text)
{
if (c == '/')
{
count++;
}
}
Step by step:
textis"/a/b/c/"countstarts at0- The loop reads the first character:
'/'- it matches
countbecomes1
- The loop reads
'a'- no match
countstays1
- The loop reads
'/'- match
countbecomes2
- The loop reads
'b'- no match
- The loop reads
'/'- match
countbecomes3
- The loop reads
'c'- no match
- The loop reads the final
'/'- match
countbecomes4
- The loop ends
Final result:
Console.WriteLine(count); // 4
Now compare that with the Replace version:
string text = "/a/b/c/";
int count = text.Length - text.Replace("/", "").Length;
Step by step:
- Original string length is
7 - Removing all
/gives"abc" - New length is
3 7 - 3 = 4
Same answer, different strategy.
Real World Use Cases
Counting characters and substrings appears in many practical tasks:
- URL parsing: count
/characters to estimate path depth - CSV or log processing: count separators like commas, tabs, or pipes
- Validation: ensure a format contains the expected number of delimiters
- Text analysis: count words, tags, mentions, or repeated markers
- Configuration parsing: count
=or:characters in key-value data - Chat and moderation tools: count forbidden words or repeated patterns
- File handling: count directory separators in paths
Example: validating a simple date format like yyyy-mm-dd
string input = "2026-06-10";
int dashCount = input.Count(c => c == '-');
if (dashCount != 2)
{
Console.WriteLine("Invalid date format");
}
Example: counting occurrences of an error label in logs
string log = "INFO... ERROR... INFO... ERROR...";
string token = "ERROR";
int count = 0;
int index = 0;
while ((index = log.IndexOf(token, index)) != -1)
{
count++;
index += token.Length;
}
Console.WriteLine(count);
Real Codebase Usage
In real codebases, developers usually choose the approach based on what they need:
For a single character
A loop or LINQ is common:
int slashCount = path.Count(c => c == '/');
This is readable and easy to maintain.
For a substring
IndexOf in a loop is a common pattern because it is explicit and flexible:
public static int CountOccurrences(string text, string value)
{
if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(value))
{
return 0;
}
int count = 0;
int index = 0;
while ((index = text.IndexOf(value, index, StringComparison.Ordinal)) != -1)
{
count++;
index += value.Length;
}
return count;
}
Common patterns in production code
- Guard clauses: return early for
nullor empty input - Explicit comparison rules: use
StringComparison.OrdinalorOrdinalIgnoreCase - Helper methods: wrap repeated logic in a utility method
- Validation: count delimiters before splitting or parsing
- Error handling: avoid fragile assumptions about input shape
Example with case-insensitive counting:
public static int CountOccurrencesIgnoreCase(string text, string value)
{
if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(value))
{
return 0;
}
int count = 0;
int index = 0;
while ((index = text.IndexOf(value, index, StringComparison.OrdinalIgnoreCase)) != -1)
{
count++;
index += value.Length;
}
return count;
}
This is the kind of reusable method you might see in utilities, parsers, or validation layers.
Common Mistakes
1. Forgetting the difference between char and string
A character uses single quotes:
'/'
A string uses double quotes:
"/"
Broken example:
if (c == "/") // wrong
{
}
Correct:
if (c == '/')
{
}
2. Dividing incorrectly when using Replace for substrings
If the substring has length greater than 1, you must divide by needle.Length.
Broken:
string text = "abc--def--";
string needle = "--";
int count = text.Length - text.Replace(needle, "").Length; // wrong result
Correct:
int count = (text.Length - text.Replace(needle, "").Length) / needle.Length;
3. Not handling empty search strings
If needle is "", the logic becomes invalid.
Safer approach:
if (string.IsNullOrEmpty(needle))
{
return 0;
}
4. Ignoring overlapping matches
Example:
string text = "aaaa";
string needle = "aa";
Possible answers:
- non-overlapping count:
2 - overlapping count:
3
You must decide which one you want.
5. Assuming Replace is always the best choice
Replace creates a new string. That can be fine for small cases, but for repeated processing or large strings, a direct search may be clearer and more efficient.
6. Forgetting case sensitivity
IndexOf can be case-sensitive or case-insensitive depending on the overload you use.
text.IndexOf(value, index, StringComparison.OrdinalIgnoreCase)
Be explicit when case matters.
Comparisons
| Approach | Best for | Readability | Handles substrings | Handles overlapping | Notes |
|---|---|---|---|---|---|
Replace length difference | Quick counting | High | Yes | No | Short, but creates a new string |
foreach loop | Single characters | Very high | No | No | Great for beginners |
LINQ Count | Single characters | High | No | No | Concise, needs System.Linq |
IndexOf loop | Substrings | High | Yes | Yes | Flexible and explicit |
| Regex | Pattern matching | Medium | Yes | Yes | Useful only when you need patterns |
foreach vs Replace
- Use
foreachwhen counting one character - Use
Replacewhen you want a quick one-liner - Use
IndexOfwhen counting substrings reliably
IndexOf vs Regex
- Use
IndexOffor literal text like"/"or"ERROR" - Use regex only when the search is a pattern, not a fixed string
Example regex:
using System.Text.RegularExpressions;
string text = "/once/upon/a/time/";
int count = Regex.Matches(text, "/").Count;
This works, but it is usually more complex than needed for simple literal counting.
Cheat Sheet
Count a single character
int count = text.Count(c => c == '/');
or
int count = 0;
foreach (char c in text)
{
if (c == '/') count++;
}
Count a substring with Replace
int count = (text.Length - text.Replace(value, "").Length) / value.Length;
Count a substring with IndexOf
int count = 0;
int index = 0;
while ((index = text.IndexOf(value, index, StringComparison.Ordinal)) != -1)
{
count++;
index += value.Length;
}
Count overlapping substrings
int count = 0;
int index = 0;
while ((index = text.IndexOf(value, index, StringComparison.Ordinal)) != -1)
{
count++;
index += 1;
}
Rules to remember
- Use
'x'forchar - Use
"text"forstring - Guard against
nullor empty search strings Replacecreates a new stringIndexOfgives more control- Decide whether overlaps should count
FAQ
What is the easiest way to count a character in a string in C#?
For a single character, a foreach loop or text.Count(c => c == '/') is usually the easiest to read.
Is using Replace to count occurrences valid in C#?
Yes. It works for many cases, especially simple one-off tasks. However, it creates a new string, so it is not always the clearest or most efficient choice.
How do I count a substring instead of a single character?
Use IndexOf in a loop or the Replace length-difference method divided by the substring length.
How do I count overlapping substring matches in C#?
Use IndexOf in a loop and increase the index by 1 after each match instead of needle.Length.
Should I use LINQ to count characters?
LINQ is a good option when you want concise code and are already using LINQ. A loop is still perfectly fine and often easier for beginners.
Does string counting handle case sensitivity automatically?
No. If case sensitivity matters, use methods like IndexOf with StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase.
What happens if the search string is empty?
You should usually treat that as a special case and return 0, because counting an empty string is not meaningful in most applications.
Mini Project
Description
Build a small C# utility that counts how many times a character or substring appears in text. This is useful in log analysis, input validation, and simple text processing tools. The project demonstrates both character counting and substring counting in a practical reusable form.
Goal
Create a reusable set of methods that can count characters, count non-overlapping substrings, and count overlapping substrings.
Requirements
- Create one method to count a single character in a string.
- Create one method to count non-overlapping substring matches.
- Create one method to count overlapping substring matches.
- Return
0fornull, empty text, or empty search values where appropriate. - Print example results using sample input strings.
Keep learning
Related questions
AddTransient vs AddScoped vs AddSingleton in ASP.NET Core Dependency Injection
Learn the differences between AddTransient, AddScoped, and AddSingleton in ASP.NET Core DI with examples and practical usage.
Best Way to Repeat a Character in C#: Building Repeated Strings Efficiently
Learn the best way to repeat a character in C#, compare StringBuilder, string concatenation, and simpler built-in options.
C# Access Modifiers and static: public, private, protected, and Defaults
Learn how C# public, private, protected, and default access control visibility, and how static differs from instance members.