C# 正则表达式零基础入门:从“看得懂“到“会用“

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

十、新手常见的几个坑

  1. 模式字符串前要加 @ :@"\d" 是"逐字字符串",\d 不会被当成转义。如果不加 @,就得写成 "\\d"。
  2. 验证整体要加 ^$:不加就是"部分匹配",只要里面藏着符合的内容就通过。
  3. 点号和小数点要分清 :. 在正则里代表"任意字符",要匹配真正的小数点,必须写成 \.。
  4. * 与 + 别混用 :要求"至少一个"用 +;* 允许 0 次,可能在没有内容时也匹配。
  5. 大小写默认敏感 :[a-z] 匹配不到大写。需要忽略大小写时,可以传 RegexOptions.IgnoreCase。
  6. 不用正则也别硬用 :简单的"是否包含",直接用 Contains 更直观、更好读。

十一、小结

这篇文章我们只做了一件事:把正则拆成四个层次,让你不再害怕它。

  • 第 1 层 固定字符:写什么找什么;
  • 第 2 层 字符类 :\d \w \s . 匹配一类字符,[] 自定义范围,大写取反;
  • 第 3 层 数量符 :? * + {n} {n,m} 规定出现几次;
  • 第 4 层 位置符 :^ 开头、$ 结尾。

四个方法:IsMatch 验证、Match 提取第一个、Matches 提取全部、Replace 替换。

学习正则不用背,只要记住"拆着读",把任何一串正则分段对照四层,你就能看懂。再亲手写几个小例子,很快就会了。

相关推荐
一比七品牌咨询1 小时前
从“方案不落地”到“产线见效”:制造企业智能化转型品牌咨询服务商有哪些
经验分享·制造·品牌咨询
ServBay1 小时前
DeepSeek 又出王炸,用 DeepInfer 搭建自己的服务是不是很容易?
后端·ai编程·deepseek
.Bryce.2 小时前
MCP+A2A+ANP,Agent通信协议的完整知识地图
java·人工智能·后端·agent·通信协议
ClouGence2 小时前
数据如何随时可用?广西水牛先生的 MySQL 实时同步实践
数据库·mysql·数据分析
JuiceFS2 小时前
面向 EB 级 AI 存储:JuiceFS 企业版架构演进
人工智能·后端
恒锐丰科技林技术员2 小时前
EG44273 低侧单通道栅极驱动芯片解析
经验分享·嵌入式硬件·硬件工程
昂好的day2 小时前
VisionMaster(VM) 标定工具详解(上):为什么必须标定 + 九点标定手眼标定
开发语言·数码相机·c#
迷途小羔羊3 小时前
项目概述
java·javascript·后端
我命由我123453 小时前
5-1.Python 编码进阶 - 函数进阶(参数、返回值、递归、函数作为参数传递、lambda 匿名函数)
java·服务器·数据库·后端·python·java-ee·python3.11