C# 正则表达式(长文讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

前言

在编程世界中,文本处理是一项高频需求。无论是验证用户输入、解析日志文件,还是提取网页中的关键信息,C# 正则表达式(Regular Expressions)都扮演着不可或缺的角色。它如同一把精密的瑞士军刀,能够高效地完成复杂文本的匹配、替换和提取任务。但对于编程初学者而言,正则表达式的语法可能显得抽象且难以掌握。本文将以循序渐进的方式,结合实际案例,帮助读者逐步理解其核心概念与应用场景,并掌握如何在 C# 中灵活运用这一工具。


二、基础语法解析:正则表达式的“基因密码”

正则表达式(Regex)是一套基于文本模式匹配的规则集合。在 C# 中,它主要通过 System.Text.RegularExpressions 命名空间下的类(如 RegexMatch 等)实现。

2.1 模式字符串与选项

正则表达式的核心是模式字符串,例如 ^\d{3}-\d{4}$ 表示“以三位数字开头,接着一个短横线,最后四位数字”。

  • ^:表示字符串的开始
  • $:表示字符串的结束
  • \d:匹配任意数字
  • {3}:指定前一个字符或组重复 3 次

代码示例:验证身份证号码格式

using System.Text.RegularExpressions;  

string idNumber = "123-4567";  
Regex pattern = new Regex(@"^\d{3}-\d{4}$");  
bool isValid = pattern.IsMatch(idNumber);  // 输出:True  

2.2 常见符号与元字符

正则表达式中的元字符(如 .*+)具有特殊含义:

  • .:匹配除换行符外的任意单个字符。
  • *:匹配前一个字符或组的零次或多次。
  • +:匹配前一个字符或组的一次或多次。
  • ?:匹配前一个字符或组的零次或一次。

比喻说明
可以把元字符想象成“文本过滤器”。例如,a.*b 表示“以 a 开头,中间任意字符,以 b 结尾”,就像用漏斗筛选符合特定形状的物体。


三、常用元字符详解:构建复杂模式的“乐高积木”

3.1 字符集与范围

通过 [] 定义字符集,例如 [abc] 匹配 a、b 或 c 中的任意一个字符。

  • [^abc]:匹配不在字符集内的字符。
  • [0-9]:匹配任意数字(等同于 \d)。

案例:验证密码强度

// 密码需包含至少1个大写字母、1个小写字母和1个数字  
Regex passwordPattern = new Regex(@"^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).+$");  
Console.WriteLine(passwordPattern.IsMatch("Abc123"));  // True  

3.2 量词与边界

量词(如 *+?)控制字符的重复次数,而边界符(如 ^$)定义匹配范围:

  • \b:匹配单词边界(例如,cat\b 匹配 "cat" 但不匹配 "catalog")。
  • .*?:非贪婪匹配(尽可能少地匹配字符)。

比喻说明
量词如同“计数器”,而边界符则像“路标”,告诉引擎在哪里开始或结束匹配。


四、进阶技巧:分组、反向引用与预定义字符类

4.1 分组与捕获

使用 () 将模式分组,便于提取或重复使用:

string text = "订单号:ORD-20231001";  
Regex regex = new Regex(@"ORD-(\d+)");  
Match match = regex.Match(text);  
Console.WriteLine(match.Groups[1].Value);  // 输出:"20231001"  

4.2 反向引用

在替换操作中,通过 \n 引用分组内容:

string original = "apple, banana, orange";  
string replaced = Regex.Replace(original, @"(\w+), (\w+)", "$2 and $1");  
// 输出:"banana and apple, orange"  

4.3 预定义字符类

C# 提供了预定义的字符类,简化模式编写:

  • \s:匹配任意空白字符(空格、制表符等)。
  • \w:匹配字母、数字或下划线(等同于 [A-Za-z0-9_])。

代码示例:提取文本中的单词

string text = "Hello World! This is a test.";  
var matches = Regex.Matches(text, @"\b\w+\b");  
foreach (Match m in matches) Console.WriteLine(m.Value);  
// 输出:Hello, World, This, is, a, test  

五、实战案例:解决真实场景中的文本处理问题

5.1 验证邮箱地址

邮箱格式复杂,需同时满足用户名、@ 符号和域名部分:

Regex emailPattern = new Regex(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$");  
Console.WriteLine(emailPattern.IsMatch("user@example.com"));  // True  

5.2 提取日期信息

从日志中提取日期并格式化:

string log = "Error occurred on 2023-10-05 at 14:30:45";  
Regex dateRegex = new Regex(@"(\d{4})-(\d{2})-(\d{2})");  
Match match = dateRegex.Match(log);  
if (match.Success)  
{  
    Console.WriteLine($"Year: {match.Groups[1].Value}");  // 2023  
    Console.WriteLine($"Month: {match.Groups[2].Value}"); // 10  
}  

5.3 替换敏感信息

用星号隐藏手机号中的中间四位:

string phone = "13812345678";  
string anonymized = Regex.Replace(phone, @"(\d{3})\d{4}(\d{4})", "$1****$2");  
// 输出:"138****5678"  

六、性能优化与常见陷阱

6.1 避免“贪婪匹配”问题

默认的 .* 是贪婪的,可能匹配到超出预期的位置。使用非贪婪模式 .*? 更安全:

string html = "<div>Content</div><span>Text</span>";  
// 错误:匹配到最后一个 </  
var greedy = Regex.Match(html, @"<.*>");  // 返回整个字符串  
// 正确:匹配最小范围  
var nonGreedy = Regex.Match(html, @"<.*?>");  // 返回 "<div>"  

6.2 预编译正则表达式

频繁使用同一模式时,建议预编译以提升性能:

static readonly Regex EmailValidator = new Regex(emailPattern);  
// 在需要时直接调用  
bool isValid = EmailValidator.IsMatch(input);  

结论

C# 正则表达式是文本处理的利器,但其强大功能也伴随着一定的学习曲线。本文通过基础语法、元字符详解、进阶技巧和实战案例,逐步拆解了正则表达式的逻辑与应用场景。建议读者通过实际编码练习加深理解,并结合工具(如在线正则表达式测试平台)调试复杂模式。

掌握正则表达式后,你将能够更高效地处理数据清洗、输入验证、日志分析等任务。记住:正则表达式并非万能,合理使用与代码逻辑结合才是关键。现在,不妨尝试用正则表达式解决你遇到的文本处理问题,让编程之路更加得心应手!

最新发布