本文介绍的是JavaScript正则表达式规则,其它语言基本相同。

一 描述字符

根据正则表达式语法规则,大部分字符仅能够描述自身,这些字符被称为普通字符,如所有的字母、数字等。
元字符就是拥有动态功能的特殊字符,需要加反斜杠进行标识,以便与普通字符和转义字符进行区别,JavaScript 正则表达式支持的元字符如表所示。

元字符 描述
. 查找单个字符,除了换行和行结束符
\w 查找单词字符
\W 查找非单词字符
\d 查找数字
\D 查找非数字字符
\s 查找空白字符
\S 查找非空白字符
\b 匹配单词边界
\B 匹配非单词边界
\n 查找换行符
\f 查找换页符
\r 查找回车符
\t 查找制表符
\v 查找垂直制表符
\xxx 查找以八进制数 xxxx 规定的字符
\xdd 查找以十六进制数 dd 规定的字符
\uxxxx 查找以十六进制 xxxx规定的 Unicode 字符([\u4e00-\u9fa5] 表示所有汉字)

二 修饰符

修饰符 可以在全局搜索中不区分大小写:

修饰符 描述
i 执行对大小写不敏感的匹配。
g 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m 执行多行匹配。

示例:
var str = ‘你好世界! Hello word!’
// 匹配任意 ASCII 字符
str.match(/[\u0000-\u00ff]/g); // [“!”, “ “, “H”, “e”, “l”, “l”, “o”, “ “, “w”, “o”, “r”, “d”, “!”]
// 匹配任意双字节的汉字
str.match(/[^\u0000-\u00ff]/g); // [“你”, “好”, “世”, “界”]
// 匹配大写字母
str.match( /[\u0041-\u004A]/g); // [“H”]

三 重复匹配

可以对于某个内容进行多次匹配

量词 描述
n+ 匹配任何包含至少一个 n 的字符串
n* 匹配任何包含零个或多个 n 的字符串
n? 匹配任何包含零个或一个 n 的字符串
n{x} 匹配包含 x 个 n 的序列的字符串
n{x,y} 匹配包含最少 x 个、最多 y 个 n 的序列的字符串
n{x,} 匹配包含至少 x 个 n 的字符串

n+ 等同于 n{1,}
n? 等同于 n{0,1}
n* 等同于 n{0,}

示例:
var str = ‘Hello helllo hehello hehehelllloooo’
str.match(/he/gi); // [“He”, “He”, “He”, “he”, “He”, “he”, “he”, “he”]
str.match(/(he)+/gi); // [“He”, “He”, “Hehe”, “Hehehehe”]
str.match(/(he)*/gi); // [“He”, “”, “”, “”, “”, “He”, “”, “”, “”, “”, “”, “Hehe”, “”, “”, “”, “”, “Hehehehe”, “”, “”, “”, “”, “”, “”, “”, “”, “”]
str.match(/(he)?/gi); // [“He”, “”, “”, “”, “”, “He”, “”, “”, “”, “”, “”, “He”, “he”, “”, “”, “”, “”, “He”, “he”, “he”, “he”, “”, “”, “”, “”, “”, “”, “”, “”, “”]
str.match(/(he){1}/gi); // [“He”, “He”, “He”, “he”, “He”, “he”, “he”, “he”]
str.match(/(he){2}/gi); // [“Hehe”, “Hehe”, “hehe”]
str.match(/(he){2,}/gi); // [“Hehe”, “Hehehehe”]
str.match(/(he){3,4}/gi); // [“Hehehehe”]
str.match(/(he)+l+/gi); // [“Hell”, “Helll”, “Hehell”, “Hehehehellll”]
str.match(/(he)+l{3,}/gi); // [“Helll”, “Hehehehellll”]

四 边界量词

匹配模式的位置

量词 描述
^ 匹配开头,在多行检测中,会匹配一行的开头
$ 匹配结尾,在多行检测中,会匹配一行的结尾

示例:
var str = ‘abc ABC’;
/^abc/gi.exec(str); // [‘abc’]
/abc$/gi.exec(str); // [‘ABC’]
/abc/gi.exec(str); // [‘abc’]
如果不添加^和$,则默认从开头匹配

五 匹配范围

表达式 描述
[abc] 查找方括号之间的任何字符。
[0-9] 查找任何从 0 至 9 的数字。
(x\ y) 查找任何以\ 分隔的选项。即x或y

示例:
var str = ‘Hello RegExp 369’
str.match(/[2-8]/gi); // [“3”, “6”]
str.match(/[el]/gi); // [“e”, “l”, “l”, “e”, “E”]
str.match(/[x|5|6]/gi); // [“x”, “6”]
str.match(/[a-h]/gi); // [“H”, “e”, “e”, “g”, “E”]
// 你也可以多个一起使用
str.match(/[2-8a-h]/gi); // [“H”, “e”, “e”, “g”, “E”, “3”, “6”]

六 转义字符

当我们需要匹配以下特殊字符时,我们需要进行转义:
$ 、 ( 、 ) 、 * 、 + 、 . 、 [ 、 ] 、 ? 、 \ 、 ^ 、 { 、 } 、 |

示例:
var str = ‘1 + 1 = 3’
str.match(/+/gi); // [“+”] 如果只需要匹配一个“+”,当你不进行转义时会报错

七 断言

假设有这样一个场景,需要在”今日18:00-20:00全场5折,洗衣液只要¥19,不要错过哦”中匹配出价格。
价格是由数字组成,如果我们只通过数字匹配的话,会把其他信息也匹配进去:
var str = ‘今日18:00-20:00全场5折,洗衣液只要¥19,不要错过哦’;
str.match(/\d+/gi); // [“18”, “00”, “20”, “00”, “5”, “19”]
如果有什么方法可以匹配指定内容的后面就好了。答案就是断言:
示例:
var str = ‘今日18:00-20:00全场5折,洗衣液只要¥19,不要错过哦’;
str.match(/(?<=¥)\d+/gi); // [“19”]

断言分为4种:
符号 描述 含义
reg(?=exp) 正向先行断言 匹配reg,且后面内容满足exp
reg(?!exp) 负向先行断言 匹配reg,且后面内容不满足exp
(?<=exp)reg 正向后发断言 匹配reg,且前面内容满足exp
(?<!exp)reg 负向后发断言 匹配reg,且前面内容不满足exp

正向先行断言
形如 A(?=B) 的形式,表示匹配到A,且A的后面是B的内容。
示例:
var str = ‘I scream, you scream, we all scream for ice-cream!’
// 匹配scream前面的一个单词
str.match(/\w+(?=\sscream)/gi); // [“I”, “you”, “all”]

负向先行断言
形如 A(?!B) 的形式,表示匹配到A,且A前面的内容不能满足B。
示例:
var str = ‘I scream, you scream, we all scream for ice-cream!’;
// 匹配scream单词,且后面不能是空格,
str.match(/scream(?!\s)/gi); // [“scream”, “scream”] 只能匹配到第一和第二个

正向后发断言
形如 (?<=B)A 的形式,表示匹配到A,且A的前面满足B。
示例:
var str = ‘I scream, you scream, we all scream for ice-cream!?’
// 匹配scream后的单词
str.match(/(?<=scream\s)\w+/gi); [“for”]

负向后发断言
形如 (?<!B)A 的形式,表示匹配到A,且A前面的不满足B。
示例:
var str = ‘I scream, you scream, we all scream for ice-cream!’;
// 匹配cream,且前面不能为字母
str.match(/(?<!\w)cream/gi); // [“cream”] 只能匹配到ice-cream中的cream

可能很多人看了之后很容易把这几个记忆混淆,这里教大家一个简单的方法理解与记忆:
断言(exp)写在后面就是匹配后面的内容,写在前面就是匹配前面的内容
正向表示满足该条件(符号 = ),负向表示不满足该条件(符号 ! )

八 分组和捕获

分组是用圆括号“()”括起来的正则表达式:
(exp) :把括号内的正则作为一个分组,系统自动分配组号,可以通过分组号引用该分组;
(?<name>exp) :定义一个命名分组,分组的正则是exp,系统为该分组分配分组号,可以通过分组名或分组号引用该分组;
(?:exp) :定义一个不捕获分组,该分组只在当前位置匹配文本,在该分组之后,无法引用该分组,因为该分组没有分组名,没有分组号,也不会占用分组编号;

1,分组编号
在正则表达式中,分组编号是自动进行的。当使用圆括号表示分组时,从正则表达式的左边开始看,看到的第一个左括号 “(” 表示第一个分组,第二个 “(“ 表示第二个分组,依次类推,需要注意的是,有一个隐含的全局分组(分组编号是0),就是整个正则表达式。默认情况下,正则表达式为每个分组自动分配一个组号,规则是:组号从1开始,从左向右,组号依次加1(base+1),例如,第一个分组的组号为1,第二个分组的组号为2,以此类推。

2,分组命名
分组不仅有编号,还能为分组设置名称,在唐库中,使用(?<name>…)为该分组设置别名(与JavaScript相同)。
注意必须是(?<name>…),括号问号小于号中间不能有空格,name是英文字符,不能是全数字。
在Python和Go语言中,使用(?P<name>…)为该分组设置别名。

3,无捕获分组
无捕获分组没有名称,也没有编号,因此,无法引用无捕获分组,无捕获分组不会占用分组编号。

示例:

out=re.match(‘\w{4,20}@(163|qq|126).com’,’test@qq.com’)
out

为分组命名,并通过别名来引用分组:

out=re.match(r”<(?P\w)><(?Ph[1-5])>.</(?P=name2)></(?P=name1)>”, “

www.baidu.com

“)
out

捕获分组和不捕获分组

对于捕获分组,findall有一个特性,就是如果结果中有捕获的分组,则将捕获的分组组成tuple返回,tuple的元素是每个分组捕获的文本。

re.findall(r’(\d{3,4}-)?(\d{7,8})’,’020-82228888\n0357-4227865’)
[(‘020-‘, ‘82228888’), (‘0357-‘, ‘4227865’)]

对于不捕获分组,findall直接返回整个匹配的结果:

re.findall(r’(?:\d{3,4}-)?\d{7,8}’,’020-82228888\n4227865’)
[‘020-82228888’, ‘4227865’]

VSCode中的查找替换:
查找:(a.b) //查找 a+任意字符+b, 用小括号括起来,表示分组1
替换:$1= //$1代表找到的分组1,替换为 匹配的分组1内容+等号

附录:常用的正则表达式

正整数: ^\d+$
负整数: ^-\d+$
电话号码: ^+?[\d\s]{3,}$
电话代码: ^+?[\d\s]+(?[\d\s]{10,}$
整数: ^-?\d+$
用户名: ^[\w\d.]{4,16}$
字母数字字符: ^[a-zA-Z0-9]$
带空格的字母数字字符: ^[a-zA-Z0-9 ]
$
密码: ^(?=^.{6,}$)((?=.[A-Za-z0-9])(?=.[A-Z])(?=.[a-z]))^.$
电子邮件: ^([a-zA-Z0-9.
%-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,4})$
IPv4 地址: ^((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))
$
小写字母: ^([a-z])$
大写字母: ^([A-Z])
$
网址: ^(((http|https|ftp):\/\/)?([[a-zA-Z0-9]-.])+(.)([[a-zA-Z0-9]]){2,4}([[a-zA-Z0-9]\/+=%&_.~?-]))$
VISA 信用卡号码: ^(4[0-9]{12}(?:[0-9]{3})?)*$
日期 (MM/DD/YYYY): ^(0?[1-9]|1[012])- /.- /.?[0-9]{2}$
日期 (YYYY/MM/DD): ^(19|20)?[0-9]{2}- /.- /.$

匹配中文字符(汉字)的正则表达式: [\u4e00-\u9fa5]
匹配双字节字符(包括汉字在内):[^x00-xff]
匹配空白行的正则表达式:nsr
匹配HTML标记的正则表达式:<(S
?)[^>]>.?|<.? />
匹配首尾空白字符的正则表达式:^s
|s$
匹配Email地址的正则表达式:w+([-+.]w+)
@w+([-.]w+).w+([-.]w+)
匹配网址URL的正则表达式:[a-zA-z]+://[^s]*
匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
匹配国内电话号码:d{3}-d{8}|d{4}-d{7}( 匹配形式如 0511-4405222 或 021-87888822)
匹配中国邮政编码:[1-9]d{5}(?!d)
匹配身份证:d{15}|d{18}

匹配特定数字:

匹配正整数 ^[1-9]d$
匹配负整数 ^-[1-9]d
$
匹配整数 ^-?[1-9]d$
匹配非负整数(正整数 + 0)^[1-9]d
|0$
匹配非正整数(负整数 + 0)^-[1-9]d|0$
匹配正浮点数 ^[1-9]d
.d|0.d[1-9]d$
匹配负浮点数 ^-([1-9]d
.d|0.d[1-9]d)$
匹配浮点数 ^-?([1-9]d
.d|0.d[1-9]d|0?.0+|0)$
匹配非负浮点数(正浮点数 +0) ^[1-9]d
.d|0.d[1-9]d|0?.0+|0$
匹配非正浮点数(负浮点数 + 0) ^(-([1-9]d
.d|0.d[1-9]d*))|0?.0+|0$

匹配特定字符串:

匹配由26个英文字母组成的字符串 ^[A-Za-z]+$
匹配由26个英文字母的大写组成的字符串 ^[A-Z]+$
匹配由26个英文字母的小写组成的字符串 ^[a-z]+$
匹配由数字和26个英文字母组成的字符串 ^[A-Za-z0-9]+$
匹配由数字、26个英文字母或者下划线组成的字符串 ^w+$
只能输入数字:^[0-9]$
只能输入n位的数字:^d{n}$
只能输入至少n位数字:^d{n,}$
只能输入m-n位的数字:^d{m,n}$
只能输入零和非零开头的数字:^(0|[1-9][0-9]
)$
只能输入有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
只能输入有1-3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
只能输入非零的正整数:^+?[1-9][0-9]$
只能输入非零的负整数:^-[1-9][0-9]
$
只能输入长度为3的字符:^.{3}$
只能输入由26个英文字母组成的字符串:^[A-Za-z]+$
只能输入由26个大写英文字母组成的字符串:^[A-Z]+$
只能输入由26个小写英文字母组成的字符串:^[a-z]+$
只能输入由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
只能输入由数字、26个英文字母或者下划线组成的字符串:^w+$
验证用户密码:^[a-zA-Z]w{5,17}$正确格式为:以字母开头,长度在6-18之间,
只能包含字符、数字和下划线。
验证是否含有^%&’’,;=?$”等字符:[^%&’’,;=?$x22]+
只能输入汉字:^[u4e00-u9fa5],{0,}$
验证Email地址:^w+[-+.]w+)@w+([-.]w+).w+([-.]w+)$
验证InternetURL:^http://([w-]+.)+[w-]+(/[w-./?%&=]
)?$
验证电话号码:^((d{3,4})|d{3,4}-)?d{7,8}$