C# 正则表达式零基础入门:别怕,从"看得懂"到"会用"
很多人第一次看到正则表达式,比如
^1[3-9]\d{9}$,会觉得这是一串"天书",看着就想跳过。其实正则一点都不神秘,它就是一套**"描述字符串长什么样"的符号**。你不用一次记住所有符号,只要把它拆成四个层次,从最简单的开始,一层一层往上加,就能看懂、也会自己写。这篇文章专门写给零基础的同学:先告诉你正则是什么、为什么需要它,再带你走完四个层次,最后用一个手机号的例子把所有知识串起来。
一、正则到底是什么?
先忘掉那些符号,我们从一个生活场景说起。
假设你在网上注册账号,需要填手机号。你随手填了一个 123,系统马上提示"手机号格式不正确"。系统是怎么知道你填错的?它一定是在拿你输入的内容,和"手机号应该长什么样"的规则做比较。
正则表达式(Regular Expression,简称 Regex)就是用来描述这条"规则"的工具。
- 用普通代码描述规则,要写一堆
if; - 用正则描述规则,就是写一串模式,比如
^1[3-9]\d{9}$。
一句话理解:正则表达式 = 用一小串符号,描述"字符串应该长什么样"。
二、为什么需要正则?
我们先用最朴素的方式,判断一个字符串里有没有数字:
csharp
string text = "abc123";
bool hasNumber = false;
// 手动遍历每个字符,判断是否为数字
foreach (char c in text)
{
if (char.IsDigit(c))
{
hasNumber = true;
break;
}
}
Console.WriteLine(hasNumber); // True
能做,但要写一个循环。同样的需求,用正则只要一行:
csharp
using System.Text.RegularExpressions;
// \d 表示"一个数字",整句意思是:text 里有没有数字
bool hasNumber = Regex.IsMatch("abc123", @"\d");
Console.WriteLine(hasNumber); // True
规则越复杂,正则越省事。比如"连续 3 个数字""以 1 开头""只能是字母"这些,用普通代码都要写不少逻辑,而正则都有现成的符号。
三、开始之前:命名空间和 Regex 类
正则相关的类都在 System.Text.RegularExpressions 命名空间里,用之前先引入:
csharp
using System;
using System.Text.RegularExpressions; // 正则所在的命名空间
核心是 Regex 类,它最常用的有四个方法,现在先混个眼熟,后面会逐个讲:
| 方法 | 干什么用 |
|---|---|
IsMatch() |
判断字符串是否符合规则(返回 true / false) |
Match() |
找到第一个符合规则的内容 |
Matches() |
找到所有符合规则的内容 |
Replace() |
把符合规则的内容替换掉 |
学习任何正则,都可以先看下面这张图。它把正则分成了四个层次,从最简单的"固定字符"开始,一层层往上加:

接下来我们就按这四层,一层一层来。
四、第 1 层:匹配固定字符
最简单的正则,就是直接写要找的文字 。比如想知道一句话里有没有 hello:
csharp
// 模式就直接写 "hello",意思是:字符串里有没有 "hello"
bool r1 = Regex.IsMatch("say hello to you", "hello");
Console.WriteLine(r1); // True(里面有 hello)
bool r2 = Regex.IsMatch("say hi", "hello");
Console.WriteLine(r2); // False(里面没有 hello)
这一层没有任何特殊符号,写什么就找什么,和你熟悉的 Contains 很像。
注意:正则默认是**"部分匹配",只要字符串里有一段**符合就行,不要求整个字符串都一样。
五、第 2 层:字符类(匹配"一类"字符)
固定字符只能找"确定的文字",但有时候我们想找的是"一类东西",比如"随便哪个数字都行""随便哪个字母都行"。这就需要字符类。
最常用的几个:
| 符号 | 含义 | 能匹配的例子 |
|---|---|---|
\d |
一个数字(0-9) | 0、7、9 |
\w |
一个字母、数字或下划线 | a、Z、5、_ |
\s |
一个空白字符(空格、制表符等) | 空格 |
. |
任意一个字符(换行除外) | 几乎所有字符 |
csharp
Console.WriteLine(Regex.IsMatch("123", @"\d")); // True(有数字)
Console.WriteLine(Regex.IsMatch("abc", @"\d")); // False(没有数字)
// . 表示任意一个字符,所以 a、中间任意、c 就能匹配
Console.WriteLine(Regex.IsMatch("a9c", @"a.c")); // True
大写表示"反过来"
\d、\w、\s 的大写版本含义正好相反:
\D:非数字;\W:非字母数字下划线;\S:非空白。
csharp
// \D 匹配"非数字",abc 里有字母,所以 True
Console.WriteLine(Regex.IsMatch("abc", @"\D")); // True
用 [ ] 自定义"我要的范围"
方括号 [] 表示"里面的任意一个字符",你可以自己列,也可以用 - 表示范围:
| 写法 | 含义 |
|---|---|
[abc] |
a、b、c 中的任意一个 |
[0-9] |
0 到 9,等价于 \d |
[a-z] |
任意一个小写字母 |
[3-9] |
3 到 9 中的任意一个数字 |
csharp
// 字符串里有没有 3~9 之间的数字
Console.WriteLine(Regex.IsMatch("5", @"[3-9]")); // True
Console.WriteLine(Regex.IsMatch("1", @"[3-9]")); // False(1 不在 3~9)
六、第 3 层:数量符(规定出现几次)
字符类只负责"一个位置上是什么",那"出现几个"怎么描述?用数量符,它跟在某个字符后面:
| 符号 | 含义 |
|---|---|
? |
0 次或 1 次 |
* |
0 次或多次 |
+ |
1 次或多次(至少一次) |
{n} |
恰好 n 次 |
{n,m} |
n 到 m 次 |
csharp
// \d{3} 表示"连续 3 个数字"
Console.WriteLine(Regex.IsMatch("123", @"\d{3}")); // True
Console.WriteLine(Regex.IsMatch("12", @"\d{3}")); // False(只有 2 个)
// a+ 表示"至少一个 a"
Console.WriteLine(Regex.IsMatch("aaa", @"a+")); // True
Console.WriteLine(Regex.IsMatch("bbb", @"a+")); // False(一个 a 都没有)
小提示:
*和+很像,区别是*允许 0 次、+要求至少 1 次。所以"必须真的有内容"时,用+更稳妥。
{n,m} 用来表示一个范围:
csharp
// \d{2,4} 表示数字长度在 2 到 4 之间
Console.WriteLine(Regex.IsMatch("12", @"\d{2,4}")); // True
Console.WriteLine(Regex.IsMatch("1234", @"\d{2,4}")); // True
Console.WriteLine(Regex.IsMatch("1", @"\d{2,4}")); // False(只有 1 位)
七、第 4 层:位置符(规定开头和结尾)
前面三层描述的都是"内容",最后一层描述的是"位置":
^:字符串的开头;$:字符串的结尾。
csharp
// ^hello:必须以 hello 开头
Console.WriteLine(Regex.IsMatch("hello world", @"^hello")); // True
Console.WriteLine(Regex.IsMatch("say hello", @"^hello")); // False(hello 不在开头)
// world$:必须以 world 结尾
Console.WriteLine(Regex.IsMatch("hello world", @"world$")); // True
为什么验证格式时一定要用 ^...$?
因为正则默认是"部分匹配",比如 \d{11} 只要字符串里"藏着"11 个连续数字就通过。加上 ^ 和 $,才表示整个字符串从头到尾都必须符合,不能多也不能少:
csharp
// 没有 ^$:只要里面藏着 3 个数字就通过
Console.WriteLine(Regex.IsMatch("abc123xyz", @"\d{3}")); // True
// 有 ^$:整个字符串必须正好是 3 个数字
Console.WriteLine(Regex.IsMatch("abc123xyz", @"^\d{3}$")); // False
到这里,四层就全部讲完了。下面这张图把常用符号汇总成一张速查表,建议保存下来随时翻:

八、综合练习:看懂手机号正则
我们把四层组合起来,看开头那串"天书" ^1[3-9]\d{9}$。现在你应该能自己"拆着读"了:
| 片段 | 属于哪一层 | 含义 |
|---|---|---|
^ |
第 4 层 | 从开头开始 |
1 |
第 1 层 | 第一位固定是数字 1 |
[3-9] |
第 2 层 | 第二位是 3 到 9 之间的数字 |
\d{9} |
第 2 + 3 层 | 数字 \d 恰好出现 9 次 |
$ |
第 4 层 | 到结尾结束 |
csharp
string phone = "13812345678";
string pattern = @"^1[3-9]\d{9}$";
bool ok = Regex.IsMatch(phone, pattern);
Console.WriteLine(ok); // True(1 开头,第二位 3,后面 9 位,共 11 位)
以后再遇到长正则,就照这个方法"拆成一小段一小段",每一段对照四层去读,就不会懵了。
九、正则的三个用途
到目前我们一直在用 IsMatch 做"验证"。正则还能做另外两件事:提取 和替换。
1. 验证:IsMatch
判断格式对不对,返回布尔值,前面已经用得很多了。
2. 提取:Match / Matches
Match() 找到第一个匹配,Matches() 找到所有匹配。比如从一句中文里把金额数字提取出来:
csharp
string text = "今天消费 128 元,停车 20 元";
// 找到所有数字(\d+ 表示一串数字)
MatchCollection matches = Regex.Matches(text, @"\d+");
foreach (Match m in matches)
{
Console.WriteLine(m.Value); // 依次输出:128、20
}
3. 替换:Replace
Replace() 把匹配到的内容替换掉。比如把字符串里的数字全部删掉:
csharp
string text = "abc123def";
// 把每一个数字替换成空字符串,相当于删除
string result = Regex.Replace(text, @"\d", "");
Console.WriteLine(result); // abcdef
十、新手常见的几个坑
- 模式字符串前要加
@:@"\d"是"逐字字符串",\d不会被当成转义。如果不加@,就得写成"\\d"。 - 验证整体要加
^$:不加就是"部分匹配",只要里面藏着符合的内容就通过。 - 点号和小数点要分清 :
.在正则里代表"任意字符",要匹配真正的小数点,必须写成\.。 *与+别混用 :要求"至少一个"用+;*允许 0 次,可能在没有内容时也匹配。- 大小写默认敏感 :
[a-z]匹配不到大写。需要忽略大小写时,可以传RegexOptions.IgnoreCase。 - 不用正则也别硬用 :简单的"是否包含",直接用
Contains更直观、更好读。
十一、小结
这篇文章我们只做了一件事:把正则拆成四个层次,让你不再害怕它。
- 第 1 层 固定字符:写什么找什么;
- 第 2 层 字符类 :
\d \w \s .匹配一类字符,[]自定义范围,大写取反; - 第 3 层 数量符 :
? * + {n} {n,m}规定出现几次; - 第 4 层 位置符 :
^开头、$结尾。
四个方法:IsMatch 验证、Match 提取第一个、Matches 提取全部、Replace 替换。
学习正则不用背,只要记住"拆着读",把任何一串正则分段对照四层,你就能看懂。再亲手写几个小例子,很快就会了。