正则表达式
“正则表达式初看像天书,但掌握后能让你少写100行代码!”
背景
正则表达式是一种用于匹配和操作文本的强大工具,它是由一系列字符和特殊字符组成的模式,用于描述要匹配的文本模式。正则表达式可以在文本中查找、替换、提取和验证特定的模式。作为一名前后端开发来说,掌握正则表达式基础用法和进阶用法必不可少。
基础语法
普通字符
普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。
| 字符 | 描述 |
|---|---|
| [ABC] | 匹配[...]中的所有字符 |
| [^ABC] | 匹配除了[...]中的所有字符 |
| [A-Z] | 匹配所有的大写字母 |
| . | 匹配除换行符的所有字符 |
| \w | 匹配字母、数字、下划线。等价于 [A-Za-z0-9_] |
| \d | 匹配任意一个阿拉伯数字(0 到 9)。等价于 [0-9] |
量词(限定词)
| 字符 | 描述 |
|---|---|
| * | 匹配前面的子表达式零次或多次 |
| + | 匹配前面的子表达式一次或多次 |
| ? | 匹配前面的子表达式零次或一次 |
| {n} | 匹配前面的子表达式n次 |
| {n,} | 至少匹配前面的子表达式n次 |
| {n,m} | 匹配前面的子表达式n到m次 |
特殊字符
| 特别字符 | 描述 |
|---|---|
| $ | 匹配输入字符串的结尾位置。要匹配$符本身,请使用\$ |
| ( ) | 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \) |
| * | 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \* |
| + | 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+ |
| . | 匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 \. |
| [ | 标记一个中括号表达式的开始。要匹配 [,请使用 \[ |
| ? | 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \? |
| \ | 将下一个字符标记为或特殊字符转义符。例如, 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '' 匹配 "",而 '(' 则匹配 "\(" |
| ^ | 匹配输入字符串的开始位置。要匹配 ^ 字符本身,请使用 \^ |
| { | 标记限定符表达式的开始。要匹配 {,请使用 \{ |
| | | 指明两项之间的一个选择。要匹配 |,请使用 \|。 |
进阶
贪婪/懒惰匹配
在正则表达式中,贪婪(Greedy)匹配和懒惰(Lazy,或非贪婪)匹配是两种重要的匹配模式,它们的区别在于 量词(如 *、+、?、{})的匹配行为。
如下例子:(可复制直接在浏览器控制台输出)
// 默认下的贪婪匹配
const text = 'abc123def456';
const greedyRegex = /\d+/; // 匹配连续数字(贪婪)
console.log(text.match(greedyRegex)[0]); // "123"(匹配所有连续数字)
// 懒惰匹配
const text = 'abc123def456';
const lazyRegex = /\d+?/; // 懒惰匹配数字
console.log(text.match(lazyRegex)[0]); // "1"(只匹配第一个数字)
适用场景:
贪婪模式:适合需要匹配 最长可能字符串 的场景(如提取整个段落)。
懒惰模式:适合需要匹配 最短可能字符串 的场景(如提取单个标签内容、避免跨节点匹配)
断言
在正则表达式中,断言(Assertion) 是一种特殊的匹配模式,它用于指定某个位置必须满足(或不满足)某种条件,但断言本身不会消耗字符(即不会成为匹配结果的一部分)。断言通常用于精细化控制匹配的边界条件
断言类型
| 类型 | 语法 | 作用 |
|---|---|---|
| 正向肯定前瞻 | (?=pattern) | 后面必须匹配 pattern |
| 正向否定前瞻 | (?!pattern) | 后面不能匹配 pattern |
| 正向肯定后顾 | (?<=pattern) | 前面必须匹配 pattern |
| 正向否定后顾 | (?<!pattern) | 前面不能匹配 pattern |
举下具体例子说明:
// 肯定前瞻
'123helloword456zxcvbnmhello'.match(/[a-z]+(?=\d+)/g) // ['helloword']
// 说明:\d+是匹配数字,[a-z]+是匹配小写字符,该正则表达式的意思是匹配一堆小写字母且后面需要携带数字,将匹配到的这个一堆小写字母输出
// 否定前瞻
'123helloword456zxcvbnmhello'.match(/[a-z]+(?!\d+)/g) // ['hellowor', 'zxcvbnmhello']
// 说明:\d+是匹配数字,[a-z]+是匹配小写字符,该正则表达式的意思是匹配一堆小写字母且后面不需要携带数字,将匹配到的这个一堆小写字母输出
// 肯定后顾
'123helloword456zxcvbnmhello'.match(/(?<=\d+)[a-z]+/g) // ['helloword', 'zxcvbnmhello']
// 说明:\d+是匹配数字,[a-z]+是匹配小写字符,该正则表达式的意思是匹配一堆数字且后面需要携带一堆小写字母,将匹配到的小写字母进行输出
// 否定后顾
'123helloword456zxcvbnmhello'.match(/(?<!\d+)[a-z]+/g) // ['elloword', 'xcvbnmhello']
// 说明:\d+是匹配数字,[a-z]+是匹配小写字符,该正则表达式的意思是匹配一堆数字且后面不需要携带一堆小写字母,将匹配到的小写字母进行输出
适用场景:
需要匹配但不想包含某些字符(如单位、符号)
数据校验:密码校验等等
替换操作(精确修改字符串) :如给所有没有单位的数字添加 px等等
反向引用
反向引用是允许你引用正则表达式中先前捕获的分组(即圆括号 () 中的内容) ,用于匹配重复的模式或验证对称结构。
正则表达式中的 (pattern) 会创建一个捕获分组,匹配的内容会被存储到编号缓冲区(从 1 开始)。
例如:/(\d+)-(\d+)/ 匹配 "123-456":
\1引用第一个分组"123"。\2引用第二个分组"456"。
好的,弄清楚基本概率后,就举例说明下:
/(\w+) \1/.test('hello hello'); // true(匹配重复的单词)
/(\w+) \1/.test('hello hell1o'); // false