Question
In C#, I tested three regular expression patterns for detecting whether a string contains a digit:
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Text.RegularExpressions;
namespace SO_RegexPerformance
{
class Program
{
static void Main(string[] args)
{
var rand = new Random(1234);
var strings = new List<string>();
// Generate 10,000 random strings
for (var i = 0; i < 10000; i++)
{
var sb = new StringBuilder();
for (var c = 0; c < 1000; c++)
{
sb.Append((char)('a' + rand.Next(26)));
}
// Add a digit to about half of the strings
if (rand.Next(2) == 0)
{
sb[rand.Next(sb.Length)] = (char)('0' + rand.Next(10));
}
strings.Add(sb.ToString());
}
var baseTime = TestPerformance(strings, @"\d");
Console.WriteLine();
var testTime = TestPerformance(strings, "[0-9]");
Console.WriteLine(" {0:P2} of first", testTime.TotalMilliseconds / baseTime.TotalMilliseconds);
testTime = TestPerformance(strings, "[0123456789]");
Console.WriteLine(" {0:P2} of first", testTime.TotalMilliseconds / baseTime.TotalMilliseconds);
}
private static TimeSpan TestPerformance(List<string> strings, string regex)
{
var sw = new Stopwatch();
int successes = 0;
var rex = new Regex(regex);
sw.Start();
foreach (var str in strings)
{
if (rex.Match(str).Success)
{
successes++;
}
}
sw.Stop();
Console.Write("Regex {0,-12} took {1} result: {2}/{3}", regex, sw.Elapsed, successes, strings.Count);
return sw.Elapsed;
}
}
}
The results showed that \d was slower than both [0-9] and [0123456789], while the latter two performed similarly.
Why does this happen?
- I expected
[0-9]to be more efficient than[0123456789]. - I also expected
\dto behave like shorthand for[0-9], but it appears to do more work.
What is the difference between \d and [0-9] in the C# regex engine, and why might \d be slower?
Short Answer
By the end of this page, you will understand why \d and [0-9] are not always equivalent in C# regular expressions, how Unicode character classes affect performance, and why benchmark results can differ even when patterns look similar. You will also learn when to prefer readability, correctness, or raw speed.
Concept
In C# regular expressions, \d is not always the same thing as [0-9].
[0-9]means: match one ASCII character in the range from'0'to'9'.[0123456789]means exactly the same set of ASCII characters, just written out explicitly.\dmeans: match a digit character class.
In .NET regex, \d is Unicode-aware by default. That means it can match more than just ASCII digits. It may also match other characters considered digits in Unicode, depending on the regex engine rules in use.
Because of that, \d can require a more general classification check than [0-9], which only needs a simple range comparison.
Why this matters
When you write regex, you are choosing between:
- meaning: what characters should match
- portability: whether behavior is consistent across systems
- performance: how much work the engine must do
If you only want ASCII digits, [0-9] is often the clearest and fastest choice.
If you want any Unicode digit recognized by .NET, \d is more correct.
Mental Model
Think of these patterns like ways of checking IDs at a door:
[0-9]is like saying: only let in people wearing badges numbered 0 through 9.[0123456789]is like keeping a printed list of the same ten badge numbers.\dis like saying: let in anyone who counts as a digit according to the full international rulebook.
The first two checks are very simple. The third check may require consulting a larger set of rules.
So \d is more flexible, but flexibility can mean extra work.
Syntax and Examples
Core syntax
var asciiRange = new Regex("[0-9]");
var explicitSet = new Regex("[0123456789]");
var digitClass = new Regex(@"\d");
Basic example
using System;
using System.Text.RegularExpressions;
class Demo
{
static void Main()
{
string input = "Room 7";
Console.WriteLine(Regex.IsMatch(input, "[0-9]")); // True
Console.WriteLine(Regex.IsMatch(input, "[0123456789]")); // True
Console.WriteLine(Regex.IsMatch(input, @"\d")); // True
}
}
All three patterns match 7 here.
Important difference in meaning
using System;
using System.Text.RegularExpressions;
class Demo
{
{
arabicIndicDigit = ;
Console.WriteLine(Regex.IsMatch(arabicIndicDigit, ));
Console.WriteLine(Regex.IsMatch(arabicIndicDigit, ));
}
}
Step by Step Execution
Example
using System;
using System.Text.RegularExpressions;
class Demo
{
static void Main()
{
string input = "abc5xyz";
var regex = new Regex("[0-9]");
bool found = regex.IsMatch(input);
Console.WriteLine(found);
}
}
What happens step by step
inputis set to"abc5xyz".- The regex pattern
[0-9]is compiled into a rule that checks whether a character is between'0'and'9'. IsMatchstarts scanning from the beginning of the string.- It checks
'a':- not in the range
0to9
- not in the range
- It checks
'b':- not in the range to
Real World Use Cases
When [0-9] is a good choice
Use [0-9] when your input format is explicitly ASCII-based:
- ZIP codes like
12345 - invoice numbers with Western digits
- version strings such as
v2 - parsing machine-generated logs
- validating numeric IDs from systems that only emit ASCII
Example:
bool containsAsciiDigit = Regex.IsMatch("OrderA7", "[0-9]");
When \d is a better choice
Use \d when you want to accept digits more generally under Unicode rules:
- internationalized user input
- apps that support multiple scripts
- text processing where digit characters may come from different writing systems
Example:
bool containsAnyDigit = Regex.IsMatch(userInput, @"\d");
When performance matters
In hot paths such as:
- processing millions of records
- scanning large log files
- validating high-throughput API input
Real Codebase Usage
In real projects, developers usually choose patterns based on intent first and performance second.
Common patterns
Explicit validation
If a field must contain ASCII digits only, teams often prefer [0-9] because it is unambiguous.
bool isValidProductCode = Regex.IsMatch(code, "^[0-9]+$");
Guard clauses
Regex is often used early in a method to reject invalid input.
if (!Regex.IsMatch(id, "^[0-9]+$"))
{
throw new ArgumentException("ID must contain ASCII digits only.");
}
Configuration and parsing
Developers often use simple classes instead of broad shorthand when parsing config files or protocol formats.
var match = Regex.Match(line, "port=([0-9]+)");
Error handling and domain rules
If an application must support localized input, \d may be the right semantic choice.
if (!Regex.IsMatch(input, @"^\d+$"))
{
;
}
Common Mistakes
1. Assuming \d always means [0-9]
This is one of the most common mistakes.
Regex.IsMatch("\u0661", @"\d"); // may be True
Regex.IsMatch("\u0661", "[0-9]"); // False
How to avoid it
Decide whether you want:
- ASCII digits only: use
[0-9] - Unicode digits: use
\d
2. Benchmarking too little work
Very small benchmarks can be noisy because of:
- JIT compilation
- memory allocation
- CPU cache effects
- regex startup cost
How to avoid it
- warm up the code first
- run multiple iterations
- isolate regex construction from matching
- use a proper benchmarking tool when possible
3. Recreating regex objects unnecessarily
Broken approach:
foreach (var str in strings)
{
var regex = new Regex();
(regex.IsMatch(str))
{
}
}
Comparisons
| Pattern | Meaning in C# regex | Typical scope | Performance tendency | Best use |
|---|---|---|---|---|
\d | Digit character class | Unicode-aware digit matching | May be slower than ASCII range checks | Internationalized digit matching |
[0-9] | ASCII digits 0 through 9 | Narrow and explicit | Often very fast | ASCII-only validation |
[0123456789] | Explicit list of ASCII digits | Same practical meaning as [0-9] | Usually similar to [0-9] | Rarely needed unless you prefer explicit listing |
[0-9] vs
Cheat Sheet
Quick reference
Digit-related patterns in C# regex
@"\d" // digit character class, Unicode-aware
"[0-9]" // ASCII digits only
"[0123456789]" // explicit ASCII digit set
Key rule
\dis not always equivalent to[0-9]in .NET.
Choose this when...
- Use
\dwhen you want Unicode digits. - Use
[0-9]when you want ASCII digits only.
Performance notes
[0-9]may be faster because it is a simple range check.[0123456789]is often optimized similarly to[0-9].\dmay do broader character classification.
Common examples
Regex.IsMatch("abc5", "[0-9]") // True
Regex.IsMatch(, )
Regex.IsMatch(, )
Regex.IsMatch(, )
FAQ
Why is \d slower than [0-9] in C#?
Because \d usually means a broader Unicode digit class, while [0-9] is only a simple ASCII range check.
Is [0123456789] slower than [0-9]?
Usually not by much. Regex engines often optimize both patterns into similar internal checks.
Does \d always match only 0 through 9?
No. In .NET, \d is generally Unicode-aware and can match digit characters beyond ASCII 0 to 9.
Which should I use: \d or [0-9]?
Use [0-9] for ASCII-only input. Use \d when you want to support Unicode digits.
Is regex the fastest way to check whether a string contains a digit?
Not always. For simple checks, a manual character loop can be faster and easier to reason about.
Why do benchmark results vary between runs?
Because of JIT compilation, CPU state, memory effects, and measurement noise. Small timing differences are not always meaningful.
Mini Project
Description
Build a small C# console program that checks whether input strings contain ASCII digits or Unicode digits, then compares the behavior of [0-9] and \d. This project helps you see that the two patterns can produce different results, which is important in validation and internationalized applications.
Goal
Create a program that tests multiple strings against both [0-9] and \d and prints which pattern matches each input.
Requirements
- Create a list of sample strings, including one with ASCII digits and one with a Unicode digit.
- Test each string using both
[0-9]and\d. - Print the input and whether each regex matched.
- Include at least one example where the two patterns produce different results.
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.
C# Type Checking Explained: typeof vs GetType() vs is
Learn when to use typeof, GetType(), and is in C#. Understand exact type checks, inheritance, and safe type testing clearly.
C# Version Numbers Explained: C# vs .NET Framework and Why “C# 3.5” Is Incorrect
Learn the correct C# version numbers, how they map to .NET releases, and why terms like C# 3.5 are inaccurate and confusing.