Question

Count Character or Substring Occurrences in a String in C#

csharpstring

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.

Take Quiz

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:

  1. text is "/a/b/c/"
  2. count starts at 0
  3. The loop reads the first character: '/'
    • it matches
    • count becomes 1
  4. The loop reads 'a'
    • no match
    • count stays 1
  5. The loop reads '/'
    • match
    • count becomes 2
  6. The loop reads 'b'
    • no match
  7. The loop reads '/'
    • match
    • count becomes 3
  8. The loop reads 'c'
    • no match
  9. The loop reads the final '/'
    • match
    • count becomes 4
  10. 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:

  1. Original string length is 7
  2. Removing all / gives "abc"
  3. New length is 3
  4. 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);
Take Quiz

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 null or empty input
  • Explicit comparison rules: use StringComparison.Ordinal or OrdinalIgnoreCase
  • 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

ApproachBest forReadabilityHandles substringsHandles overlappingNotes
Replace length differenceQuick countingHighYesNoShort, but creates a new string
foreach loopSingle charactersVery highNoNoGreat for beginners
LINQ CountSingle charactersHighNoNoConcise, needs System.Linq
IndexOf loopSubstringsHighYesYesFlexible and explicit
RegexPattern matchingMediumYesYesUseful only when you need patterns

foreach vs Replace

  • Use foreach when counting one character
  • Use Replace when you want a quick one-liner
  • Use IndexOf when counting substrings reliably

IndexOf vs Regex

  • Use IndexOf for 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.

Take Quiz

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' for char
  • Use "text" for string
  • Guard against null or empty search strings
  • Replace creates a new string
  • IndexOf gives 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.

Take Quiz

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 0 for null, empty text, or empty search values where appropriate.
  • Print example results using sample input strings.
Take Quiz

Keep learning

Related questions

View more