C# 正则表达式:量词与锚点——从“.*”到精确匹配
2025年12月21日 21:59
一、量词:告诉引擎“要重复多少次”
量词出现在一个“单元”后面,表示这个单元要重复多少次。
单元可以是:
- 一个普通字符:
a - 一个字符类:
\d、[A-Z] - 一个分组:
(ab)
1. 常见量词一览
-
?:0 或 1 次 -
*:0 次或多次 -
+:1 次或多次 -
{n}:恰好 n 次 -
{n,}:至少 n 次 -
{n,m}:n 到 m 次之间
示例:
using System.Text.RegularExpressions;
string pattern = @"^\d{3,5}$";
bool ok = Regex.IsMatch("1234", pattern); // True
2. 量词是作用在“前一项”上的
注意:ab+ 只会把 + 作用到 b 上:
- 模式:
ab+- 匹配:"ab"、"abb"、"abbbb"...
- 模式:
(ab)+- 匹配:"ab"、"abab"、"ababab"...
也就是说,当你想对一“串”东西使用量词,一定要用括号分组。
二、贪婪 vs 懒惰:* 和 *? 的根本区别
量词在默认情况下是“贪婪”的:
- 在不影响匹配成功的前提下,尽可能多地吃字符。
1. 贪婪匹配:.*
string input = "<tag>content</tag><tag>more</tag>";
string pattern = @"<tag>.*</tag>";
Match m = Regex.Match(input, pattern);
Console.WriteLine(m.Value);
匹配结果:
<tag>content</tag><tag>more</tag>
-
.*会尽可能多地吃字符,直到最后一个满足条件的</tag>。
2. 懒惰匹配:.*?
在量词后面再加一个 ?,就变成“懒惰”(即最多满足一次):
-
*?:尽可能少的 0 次或多次 -
+?:尽可能少的 1 次或多次 -
??:尽可能少的 0 或 1 次 -
{n,m}?:在 n~m 之间,尽量少
改写上面的例子:
string pattern = @"<tag>.*?</tag>";
Match m = Regex.Match(input, pattern);
Console.WriteLine(m.Value);
匹配结果:
<tag>content</tag>
三、用量词写几个常见“格式”:
1. 简单日期:yyyy-MM-dd
^\d{4}-\d{2}-\d{2}$
- 不考虑合法性,只看格式
2. 用户名:字母开头,后面 3~15 位字母数字下划线
^[A-Za-z]\w{3,15}$
-
[A-Za-z]:首字符必须是字母 -
\w{3,15}:后面 3~15 个字母数字下划线 - 总长度:4~16
C#:
string pattern = @"^[A-Za-z]\w{3,15}$";
bool ok = Regex.IsMatch("User_001", pattern);
3. 整数和小数
简单版本的“非负整数或小数”:
^\d+(\.\d+)?$
-
\d+:至少一位数字 -
(\.\d+)?:可选的小数部分(.+ 至少一位数字)
匹配:0、123、3.14、0.5
不匹配:.、.5、3.(如果你想放宽,可以调整)。
四、锚点:决定“匹配的是不是整串”
锚点(Anchor)是一类特殊的“零宽”匹配,只匹配“位置”,不消耗字符。
1)^:开头,$:结尾
默认情况下:
-
^匹配字符串的开头; -
$匹配字符串的结尾。
示例:
^abc // 匹配以 "abc" 开头的字符串
abc$ // 匹配以 "abc" 结尾的字符串
^abc$ // 字符串只能是 "abc"
Regex.IsMatch("abc123", @"^abc"); // True
Regex.IsMatch("123abc", @"abc$"); // True
Regex.IsMatch("xabcx", @"^abc$"); // False
2. 表单校验一定要写 ^ 和 $
// 不严谨
Regex.IsMatch("abc2025-12-18xyz", @"\d{4}-\d{2}-\d{2}");
// True,只要“包含”符合格式的子串就通过
// 严谨
Regex.IsMatch("abc2025-12-18xyz", @"^\d{4}-\d{2}-\d{2}$");
// False,整个字符串不是完整日期
3. 字符类里的 ^ 意义完全不同
^abc // 锚点:开头
[^abc] // 取反:匹配任何不是a、b、c的字符
-
^在[]外:开头锚点 -
^在[]里且在首位:表示“取反”,方括号内的字符任意组合的反面
五、单词边界:\b
\b 是“单词边界”(word boundary),匹配“从一个 \w 字符到一个非 \w 字符的边界”。
例子:
string text = "cat scat category";
string pattern = @"\bcat\b";
MatchCollection matches = Regex.Matches(text, pattern);
foreach (Match m in matches)
{
Console.WriteLine(m.Value);
}
输出:
cat
解析:
-
"cat"前后都是边界(左边是开头,右边是空格),满足\b。 -
"scat"中的cat左边是s,属于\w,不会被\bcat\b匹配。 -
"category"中的cat右边是e,也是\w,也不符合。
六、多行模式(Multiline)与单行模式(Singleline)
C# 中用 RegexOptions 可以控制 ^ / $ 和 . 的行为。
1. RegexOptions.Multiline:多行模式
默认情况:
string text = "first\nsecond\nthird";
string pattern = @"^second$";
Console.WriteLine(Regex.IsMatch(text, pattern)); // False
因为:
-
^和$在默认模式下只匹配整个字符串起始和结尾,不会感知行。
多行模式开启后:
bool ok = Regex.IsMatch(
text,
pattern,
RegexOptions.Multiline
);
Console.WriteLine(ok); // True
此时:
-
^/$会匹配每一行的开头/结尾(以\n作为换行)。
2. RegexOptions.Singleline:单行模式 / DOTALL
默认情况下:
-
.不匹配换行符。
string text = "line1\nline2";
string pattern = @".*";
Match m1 = Regex.Match(text, pattern);
Console.WriteLine(m1.Value); // "line1"
开启 Singleline 后:
Match m2 = Regex.Match(text, pattern, RegexOptions.Singleline);
Console.WriteLine(m2.Value); // "line1\nline2"
此时:
-
.会匹配包括换行在内的任何字符。
总结一下:
-
Multiline:影响^/$,让它们感知“行” -
Singleline:影响.,让.能匹配换行
它们可以一起用:
var regex = new Regex(
pattern,
RegexOptions.Multiline | RegexOptions.Singleline
);
结语
点个赞,关注我获取更多实用 C# 技术干货!如果觉得有用,记得收藏本文