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
命名空间下的类(如 Regex
、Match
等)实现。
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# 正则表达式是文本处理的利器,但其强大功能也伴随着一定的学习曲线。本文通过基础语法、元字符详解、进阶技巧和实战案例,逐步拆解了正则表达式的逻辑与应用场景。建议读者通过实际编码练习加深理解,并结合工具(如在线正则表达式测试平台)调试复杂模式。
掌握正则表达式后,你将能够更高效地处理数据清洗、输入验证、日志分析等任务。记住:正则表达式并非万能,合理使用与代码逻辑结合才是关键。现在,不妨尝试用正则表达式解决你遇到的文本处理问题,让编程之路更加得心应手!