C# (.NET) Regular Expressions Classic Cases
Deep dive into C# regular expressions, master Regex class advanced usage and practical application cases.
C#'s System.Text.RegularExpressions namespace provides powerful regex support. This guide will take you from basics to advanced, covering everything you need to know about C# regular expressions.
.NET Regex Basics
Namespace Import
using System;
using System.Text.RegularExpressions;
Core Class
C#'s regex functionality is primarily implemented through the Regex class, providing rich static and instance methods.
Basic Usage
1. Creating Regex Objects
// Use static method
bool isMatch = Regex.IsMatch("123", @"\d+");
// Create instance
Regex regex = new Regex(@"\d+");
bool isMatch2 = regex.IsMatch("123");
// Use options
Regex caseInsensitive = new Regex(@"hello", RegexOptions.IgnoreCase);
2. Regex Options
// Ignore case
Regex regex1 = new Regex(@"hello", RegexOptions.IgnoreCase);
// Multi-line mode
Regex regex2 = new Regex(@"^test$", RegexOptions.Multiline);
// Single-line mode (dot matches newline)
Regex regex3 = new Regex(@".*", RegexOptions.Singleline);
// Ignore whitespace in pattern
Regex regex4 = new Regex(@"
\d{4} # Year
- # Separator
\d{2} # Month
- # Separator
\d{2} # Day
", RegexOptions.IgnorePatternWhitespace);
// Explicit capture
Regex regex5 = new Regex(@"(?<name>\w+)", RegexOptions.ExplicitCapture);
// Compile regex (improves performance)
Regex regex6 = new Regex(@"\d+", RegexOptions.Compiled);
// Combine options
Regex regex7 = new Regex(@"test",
RegexOptions.IgnoreCase | RegexOptions.Multiline);
Matching Operations
1. IsMatch - Check If Match Exists
string text = "Hello 123 World";
// Static method
bool hasNumber = Regex.IsMatch(text, @"\d+");
Console.WriteLine(hasNumber); // True
// Instance method
Regex regex = new Regex(@"\d+");
bool hasNumber2 = regex.IsMatch(text);
Console.WriteLine(hasNumber2); // True
2. Match - Single Match
string text = "abc123def456";
// Match first occurrence
Match match = Regex.Match(text, @"\d+");
if (match.Success) {
Console.WriteLine(match.Value); // 123
Console.WriteLine(match.Index); // 3
Console.WriteLine(match.Length); // 3
}
3. Matches - Global Match
string text = "abc123def456ghi789";
MatchCollection matches = Regex.Matches(text, @"\d+");
foreach (Match match in matches) {
Console.WriteLine($"Found: {match.Value}, Position: {match.Index}");
}
// Output:
// Found: 123, Position: 3
// Found: 456, Position: 9
// Found: 789, Position: 15
Group Capturing
1. Basic Grouping
string text = "2024-01-25";
Match match = Regex.Match(text, @"(\d{4})-(\d{2})-(\d{2})");
if (match.Success) {
Console.WriteLine(match.Groups[0].Value); // 2024-01-25
Console.WriteLine(match.Groups[1].Value); // 2024
Console.WriteLine(match.Groups[2].Value); // 01
Console.WriteLine(match.Groups[3].Value); // 25
}
2. Named Groups
string text = "2024-01-25";
Match match = Regex.Match(text,
@"(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})");
if (match.Success) {
Console.WriteLine(match.Groups["year"].Value); // 2024
Console.WriteLine(match.Groups["month"].Value); // 01
Console.WriteLine(match.Groups["day"].Value); // 25
}
3. Group Capture Collections
string text = "Colors: red, green, blue";
Match match = Regex.Match(text, @"(?:,\s*)(\w+)");
if (match.Success) {
CaptureCollection captures = match.Groups[1].Captures;
foreach (Capture capture in captures) {
Console.WriteLine(capture.Value);
}
// Output:
// green
// blue
}
Replacement Operations
1. Replace - Simple Replacement
string text = "Price: 100, 200, 300";
string result = Regex.Replace(text, @"\d+", "[number]");
Console.WriteLine(result);
// Output: Price: [number], [number], [number]
// Limit replacement count
result = Regex.Replace(text, @"\d+", "[number]", 2);
Console.WriteLine(result);
// Output: Price: [number], [number], 300
2. Use Group References
string text = "2024-01-25";
string result = Regex.Replace(text, @"(\d{4})-(\d{2})-(\d{2})", "$2/$3/$1");
Console.WriteLine(result);
// Output: 01/25/2024
3. MatchEvaluator - Callback Replacement
string text = "Price: 100, 200, 300";
string result = Regex.Replace(text, @"\d+", match => {
int num = int.Parse(match.Value);
return (num * 0.9).ToString();
});
Console.WriteLine(result);
// Output: Price: 90, 180, 270
Splitting Operations
1. Split - Split String
string text = "apple,banana;orange|grape";
string[] fruits = Regex.Split(text, "[,;|]");
foreach (string fruit in fruits) {
Console.WriteLine(fruit);
}
// Output:
// apple
// banana
// orange
// grape
// Limit split count
string[] parts = Regex.Split(text, "[,;|]", 2);
// Output: ["apple", "banana;orange|grape"]
Advanced Features
1. Lookahead and Lookbehind
string text = "Price: 100元, 200元";
// Positive lookahead (match numbers followed by "元")
string pattern = @"\d+(?=元)";
foreach (Match match in Regex.Matches(text, pattern)) {
Console.WriteLine(match.Value);
}
// Output: 100, 200
// Negative lookahead (match numbers not followed by "元")
text = "Count: 100, Price: 200元";
pattern = @"\d+(?!元)";
foreach (Match match in Regex.Matches(text, pattern)) {
Console.WriteLine(match.Value);
}
// Output: 100
// Positive lookbehind (match numbers preceded by "Price:")
text = "Price: 100, Count: 200";
pattern = @"(?<=Price:)\d+";
foreach (Match match in Regex.Matches(text, pattern)) {
Console.WriteLine(match.Value);
}
// Output: 100
2. Boundary Matching
string text = "hello world hello";
// \b - Word boundary
string pattern = @"\bhello\b";
foreach (Match match in Regex.Matches(text, pattern)) {
Console.WriteLine(match.Value);
}
// Output: hello, hello (two standalone words)
// \B - Non-word boundary
pattern = @"\Bhello\B";
var matches = Regex.Matches(text, pattern);
Console.WriteLine(matches.Count); // 0
3. Greedy vs Non-greedy
string text = "<div>content1</div><div>content2</div>";
// Greedy match (default)
string pattern = @"<div>.*</div>";
Match match = Regex.Match(text, pattern);
Console.WriteLine(match.Value);
// Output: <div>content1</div><div>content2</div>
// Non-greedy match
pattern = @"<div>.*?</div>";
match = Regex.Match(text, pattern);
Console.WriteLine(match.Value);
// Output: <div>content1</div>
Practical Examples
Example 1: Validate Email Address
using System;
using System.Text.RegularExpressions;
public class EmailValidator
{
private static readonly Regex EmailPattern = new Regex(
@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
RegexOptions.Compiled
);
public static bool IsValid(string email)
{
if (string.IsNullOrEmpty(email))
return false;
return EmailPattern.IsMatch(email);
}
public static void Main()
{
Console.WriteLine(IsValid("[email protected]")); // True
Console.WriteLine(IsValid("invalid.email")); // False
Console.WriteLine(IsValid("user@domain")); // False
}
}
Example 2: Extract All Emails
using System;
using System.Text.RegularExpressions;
public class EmailExtractor
{
private static readonly Regex EmailPattern = new Regex(
@"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
RegexOptions.Compiled
);
public static string[] Extract(string text)
{
MatchCollection matches = EmailPattern.Matches(text);
string[] emails = new string[matches.Count];
for (int i = 0; i < matches.Count; i++)
{
emails[i] = matches[i].Value;
}
return emails;
}
public static void Main()
{
string text = "Contact: [email protected], [email protected], [email protected]";
string[] emails = Extract(text);
foreach (string email in emails)
{
Console.WriteLine(email);
}
}
}
Example 3: Validate Phone Number
public class PhoneValidator
{
private static readonly Regex PhonePattern = new Regex(
@"^1[3-9]\d{9}$",
RegexOptions.Compiled
);
public static bool IsValid(string phone)
{
if (string.IsNullOrEmpty(phone))
return false;
return PhonePattern.IsMatch(phone);
}
public static void Main()
{
Console.WriteLine(IsValid("13812345678")); // True
Console.WriteLine(IsValid("12345678901")); // False
Console.WriteLine(IsValid("1381234567")); // False
}
}
Example 4: ID Card Validation
public class IDCardValidator
{
private static readonly Regex IDPattern = new Regex(
@"^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$",
RegexOptions.Compiled
);
public static bool IsValid(string idCard)
{
if (string.IsNullOrEmpty(idCard))
return false;
return IDPattern.IsMatch(idCard);
}
public static void Main()
{
Console.WriteLine(IsValid("11010519900307233X")); // True
Console.WriteLine(IsValid("123456789012345678")); // False
}
}
Example 5: HTML Cleanup
public class HtmlCleaner
{
private static readonly Regex TagPattern = new Regex(
@"<[^>]+>",
RegexOptions.Compiled
);
public static string StripHTML(string html)
{
return TagPattern.Replace(html, "");
}
public static void Main()
{
string html = "<p>Hello <b>World</b>!</p>";
string clean = StripHTML(html);
Console.WriteLine(clean);
// Output: Hello World!
}
}
Example 6: URL Parsing
public class URLParser
{
private static readonly Regex URLPattern = new Regex(
@"^(?<protocol>https?)://(?<host>[^/]+)(?<path>/[^?]*)?(?<query>\?.*)?$",
RegexOptions.Compiled
);
public static void Parse(string url)
{
Match match = URLPattern.Match(url);
if (match.Success)
{
Console.WriteLine($"Protocol: {match.Groups["protocol"].Value}");
Console.WriteLine($"Host: {match.Groups["host"].Value}");
Console.WriteLine($"Path: {match.Groups["path"].Value}");
Console.WriteLine($"Query: {match.Groups["query"].Value}");
}
}
public static void Main()
{
Parse("https://example.com/path/to/page?name=John&age=30");
// Output:
// Protocol: https
// Host: example.com
// Path: /path/to/page
// Query: ?name=John&age=30
}
}
Example 7: Log Parsing
public class LogAnalyzer
{
private static readonly Regex LogPattern = new Regex(
@"^(?<date>\d{4}-\d{2}-\d{2})\s+(?<time>\d{2}:\d{2}:\d{2})\s+\[(?<level>\w+)\]\s+(?<message>.+)$",
RegexOptions.Compiled | RegexOptions.Multiline
);
public static LogEntry Parse(string logLine)
{
Match match = LogPattern.Match(logLine);
if (!match.Success)
return null;
return new LogEntry
{
Date = match.Groups["date"].Value,
Time = match.Groups["time"].Value,
Level = match.Groups["level"].Value,
Message = match.Groups["message"].Value
};
}
public static void Main()
{
string log = "2024-01-25 10:30:45 [INFO] User login successful";
LogEntry entry = Parse(log);
if (entry != null)
{
Console.WriteLine($"Time: {entry.Date} {entry.Time}");
Console.WriteLine($"Level: {entry.Level}");
Console.WriteLine($"Message: {entry.Message}");
}
}
}
public class LogEntry
{
public string Date { get; set; }
public string Time { get; set; }
public string Level { get; set; }
public string Message { get; set; }
}
Example 8: Data Masking
public class DataMasker
{
private static readonly Regex PhoneMask = new Regex(
@"(?<=\d{3})\d{4}(?=\d{4})",
RegexOptions.Compiled
);
private static readonly Regex IDMask = new Regex(
@"(?<=^\d{6})\d{8}(?=\d{4}$)",
RegexOptions.Compiled
);
public static string MaskPhone(string phone)
{
return PhoneMask.Replace(phone, "****");
}
public static string MaskIDCard(string idCard)
{
return IDMask.Replace(idCard, "********");
}
public static void Main()
{
Console.WriteLine(MaskPhone("13812345678")); // 138****5678
Console.WriteLine(MaskIDCard("11010519900307233X")); // 110105********233X
}
}
Example 9: Password Strength Validation
public class PasswordValidator
{
public static ValidationResult Validate(string password)
{
var result = new ValidationResult();
if (string.IsNullOrEmpty(password) || password.Length < 8)
{
result.IsValid = false;
result.Message = "Password must be at least 8 characters";
return result;
}
if (!Regex.IsMatch(password, @"[A-Z]"))
{
result.IsValid = false;
result.Message = "Password must contain uppercase letters";
return result;
}
if (!Regex.IsMatch(password, @"[a-z]"))
{
result.IsValid = false;
result.Message = "Password must contain lowercase letters";
return result;
}
if (!Regex.IsMatch(password, @"\d"))
{
result.IsValid = false;
result.Message = "Password must contain numbers";
return result;
}
if (!Regex.IsMatch(password, @"[!@#$%^&*]"))
{
result.IsValid = false;
result.Message = "Password must contain special characters";
return result;
}
result.IsValid = true;
result.Message = "Password meets requirements";
return result;
}
public static void Main()
{
string password = "MyPass123!";
var result = Validate(password);
Console.WriteLine($"{result.IsValid} - {result.Message}");
}
}
public class ValidationResult
{
public bool IsValid { get; set; }
public string Message { get; set; }
}
Example 10: Extract IP Addresses
public class IPExtractor
{
private static readonly Regex IPv4Pattern = new Regex(
@"\b(?:\d{1,3}\.){3}\d{1,3}\b",
RegexOptions.Compiled
);
private static readonly Regex IPv6Pattern = new Regex(
@"\b(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\b",
RegexOptions.Compiled
);
public static string[] ExtractIPv4(string text)
{
MatchCollection matches = IPv4Pattern.Matches(text);
string[] ips = new string[matches.Count];
for (int i = 0; i < matches.Count; i++)
{
ips[i] = matches[i].Value;
}
return ips;
}
public static string[] ExtractIPv6(string text)
{
MatchCollection matches = IPv6Pattern.Matches(text);
string[] ips = new string[matches.Count];
for (int i = 0; i < matches.Count; i++)
{
ips[i] = matches[i].Value;
}
return ips;
}
public static void Main()
{
string text = "Server: 192.168.1.1, Backup: 10.0.0.1, External: 8.8.8.8";
string[] ipv4s = ExtractIPv4(text);
foreach (string ip in ipv4s)
{
Console.WriteLine(ip);
}
}
}
Best Practices
1. Use Static Readonly Fields
// Good practice: Pre-compile and cache
public class Validators
{
private static readonly Regex EmailPattern = new Regex(
@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
RegexOptions.Compiled
);
public static bool IsValidEmail(string email)
{
return EmailPattern.IsMatch(email);
}
}
// Bad practice: Create new instance every time
public static bool IsValidEmailBad(string email)
{
return new Regex(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").IsMatch(email);
}
2. Use @ String Literals
// Good practice: Use @ string
Regex pattern = new Regex(@"\d+");
// Bad practice: Need escaping
Regex pattern = new Regex("\\d+");
3. Check Null Values
public static bool IsValid(string input)
{
if (string.IsNullOrEmpty(input))
return false;
return Pattern.IsMatch(input);
}
4. Use Named Groups
// Good practice: Named groups
string pattern = @"(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})";
Match match = Regex.Match(text, pattern);
string year = match.Groups["year"].Value;
// Bad practice: Index groups
string pattern = @"(\d{4})-(\d{2})-(\d{2})";
Match match = Regex.Match(text, pattern);
string year = match.Groups[1].Value;
5. Use RegexOptions.Compiled
// Good practice: Compile regex
private static readonly Regex Pattern = new Regex(
@"\d+",
RegexOptions.Compiled
);
// Bad practice: Don't compile
private static readonly Regex Pattern = new Regex(@"\d+");
Performance Optimization
1. Avoid Repeated Compilation
// Efficient
private static readonly Regex Pattern = new Regex(@"\d+", RegexOptions.Compiled);
public void ProcessTexts(string[] texts)
{
foreach (string text in texts)
{
Pattern.Matches(text);
}
}
// Inefficient
public void ProcessTextsBad(string[] texts)
{
foreach (string text in texts)
{
new Regex(@"\d+").Matches(text);
}
}
2. Use More Specific Patterns
// Efficient
Regex pattern = new Regex(@"\d{3}-\d{4}-\d{4}");
// Inefficient
Regex pattern = new Regex(@".+");
3. Limit Match Count
// Limit result count, avoid wasting memory
MatchCollection matches = Regex.Matches(text, @"\d+");
for (int i = 0; i < Math.Min(10, matches.Count); i++)
{
// Process first 10 matches
}
Common Pitfalls
1. Forgetting to Use @ String
// Error: No @ used
Regex pattern = new Regex("\d+");
// Correct
Regex pattern = new Regex(@"\d+");
2. Not Checking Match Results
// Error: Might be null
Match match = Regex.Match(text, pattern);
Console.WriteLine(match.Value);
// Correct
Match match = Regex.Match(text, pattern);
if (match.Success)
{
Console.WriteLine(match.Value);
}
3. Forgetting to Escape Replacement Strings
// Error: $1 interpreted as group reference
string result = Regex.Replace(text, @"\d+", "$1");
// Correct: Escape $
string result = Regex.Replace(text, @"\d+", @"$1");
4. Overusing Greedy Matching
// Error: Greedy match
Regex pattern = new Regex("<div>.*</div>");
// Correct: Non-greedy match
Regex pattern = new Regex("<div>.*?</div>");
Summary
C# regex provides powerful and flexible functionality:
Core class: Regex - Provides static and instance methods
Main methods:
IsMatch()- Check if match existsMatch()- Single matchMatches()- Global matchReplace()- ReplacementSplit()- Splitting
Advanced features:
- Named groups
- Lookahead and lookbehind
- Boundary matching
- MatchEvaluator callback
Best practices:
- Pre-compile regular expressions
- Use @ string literals
- Check null values and match results
- Use named groups
- Use RegexOptions.Compiled
Master these techniques, and you can efficiently handle various text processing tasks in C#!
Use our online Regex Tester to test C# regex patterns!
About the Author
The Regex Master Team consists of experienced developers and technical writers dedicated to simplifying regular expressions for everyone. We ensure all patterns are rigorously tested and verified to provide accurate, production-ready solutions.
Try It: Regex Tester
Use our interactive regex tester to experiment with the patterns you learned in this article. Test your regular expressions in real-time and see immediate results.