正则表达式简介

在动手写正则之前,我们先认识它到底是什么、从哪儿来、为什么几乎所有程序员都要学。理解了背景,后面学具体语法时会更有的放矢。

什么是正则表达式

正则表达式(Regular Expression,常缩写为 regexregexp)是一种描述"字符串模式"的微型语言。用一串特殊符号定义一个模式,然后让计算机在文本里找出所有匹配它的部分。

举个最直观的例子:你想从一段文本里找出所有"三位数字",普通字符串查找做不到——你不知道具体是哪三个数字。但用正则写一个模式就能匹配一整类字符串。看下面的代码:

// 用正则匹配一个固定的字符串 "cat"
const re = /cat/;
re.test("I have a cat.");        // true
"I have a cat.".search(re);      // 9 (返回位置)
"catdog".replace(re, "dog");     // "dogdog"

// 加一个元字符,能力立刻翻倍
/ca./.test("cat cab can");       // true (. 表示任意一个字符)
/cat|dog/.test("I have a dog");  // true (| 表示"或")

第一行 /cat/ 是最简单的正则,只能匹配固定的 "cat"。但加上 .(任意字符)、|(或)这些元字符(metacharacter),表达能力立刻翻倍——这正是正则的精髓:用最少的字符,表达最丰富的模式

正则的诞生历史

正则不是程序员拍脑袋想出来的,它有深厚的数学根基。1956 年,数学家 Stephen Kleene 在研究神经网络形式模型时发表论文,提出了"正则集合"(regular sets)的概念和正则代数,这是正则表达式的数学原型。名字里的 "regular" 是数学术语(正则语言),不是"经常"的意思。中文也译作"正规表示式"或"规则表达式"。

把这个数学概念带进编程世界的是 Ken Thompson——Unix 之父之一、C 语言前身 B 语言的发明人、后来也是 Go 语言的三位设计者之一。1968 年,他在 qed 文本编辑器里实现了正则搜索功能,从此正则进入实用阶段。之后他又把这个能力做进了 Unix 的传奇工具 grep(名字来自 g/re/p 命令,意为"全局正则打印")。

到 1986 年,Perl 语言把正则推向了巅峰,扩展了大量新特性(非贪婪匹配、零宽断言等),形成了今天大家熟悉的"Perl 风格正则"(PCRE)。后来 POSIX 标准、JavaScript、Python、Java、.NET 都借鉴了 PCRE,虽然细节各有差异,但核心语法 90% 通用。

正则用在哪

凡是要"在文本里找规律"的场景,几乎都离不开正则。下面是几个最高频的实际用途:

// 1. 表单校验:前端检查邮箱格式
const emailRe = /^[\w.+-]+@[\w-]+(\.[\w-]+)+$/;
emailRe.test("user@example.com");   // true
emailRe.test("not-an-email");       // false

// 2. 字符串提取:从日志里抓出所有 IP
const log = "来自 192.168.1.1 和 10.0.0.2 的请求";
const ips = log.match(/\d{1,3}(\.\d{1,3}){3}/g);
// ["192.168.1.1", "10.0.0.2"]

// 3. 替换:把 Markdown 链接转成 HTML
"[Google](https://google.com)".replace(/\[(.+?)\]\((.+?)\)/, '<a href="$2">$1</a>');
// 输出: <a href="https://google.com">Google</a>

// 4. 分割:用任意空白或标点切句子
"a,b;c d".split(/[,;\s]+/);     // ["a","b","c","d"]

不同语言的写法差异

同一个"匹配一串数字"的正则,在不同语言里语法略有不同,但核心模式本身完全一样。下面用四种语言对照,让你直观感受:

// 不同语言里写正则的差异(同一个"匹配数字"模式)

// JavaScript:字面量用斜杠包裹
/\d+/.test("a123b");   // true

// Python:用 r 前缀字符串 + re 模块
import re
re.findall(r"\d+", "a123b");   // ['123']

// Java:字符串写法,反斜杠要双倍
"123".matches("\\d+");        // true

// Go:用反引号原生字符串
regexp.MustCompile(`\d+`).MatchString("a123b")   // true

// 核心语法(元字符、量词、分组)90% 相同
// 差异主要在"标志位"和"高级特性(如后行断言)"

关键差异通常只在两点:字面量怎么包裹(JS 用斜杠、Python 用 r 字符串、Java 用普通字符串、Go 用反引号)和反斜杠转义层数。Java/JS 字符串里要写双反斜杠才能给引擎一个,Python 的 r"" 原生字符串和 Go 的反引号字符串则所见即所得,更省事。

为什么"看上去像乱码"

第一次看到一长串正则,大多数人都会头皮发麻。这是因为正则设计于 1960 年代,追求极致紧凑——能用一个符号就不用两个字符。但这种紧凑也带来了它出名的弱点:可读性差,同行戏称"写出来一秒钟,看懂两小时"。

本系列的策略是从小块开始:每篇只讲 1~2 个新概念,配可运行代码,让你慢慢建立对每个元字符的直觉。读到最后一篇时,回头看当初像天书的串,你会发现它其实清晰可拆解。

该不该学正则

结论是必须学。哪怕你不想成为"正则高手",日常开发中也至少要能看懂别人写的、能改简单的、知道什么场景该用。正则是那种"两周不用就忘光"的技能,但熟练后能省下大量字符串处理代码——别人写 30 行 if/else 切分字符串,你一行正则搞定。

建议学法:每读一篇就打开浏览器控制台(按 F12),把代码粘进去跑一遍,改改参数看输出。配套使用 regex101.com 这个神器,实时高亮匹配、解释每段含义,调试必备。

← 返回 正则教程目录

下一篇 基础语法与元字符

✈️💬