正则表达式

https://blog.csdn.net/LLLLQZ/article/details/118278287
https://regex101.com/

元字符

普通字符

123abc

反斜杠\

把普通字符转义成特殊用法,具体见下面预定义的字符集
把特殊字符转义成普通输出,像\[\]\{\}\(\)\[\]\?\+\*\.\^\$\|输出就是[]{}()[]?+*.^$|

点运算符 .

.匹配任意单个字符,但不匹配换行符和回车符。
例如,表达式.ar匹配一个任意字符后面跟着是a和r的字符串。

*号

匹配 在*之前的字符出现大于等于0次。
例如,表达式 a* 匹配0或更多个以a开头的字符。表达式[a-z]* 匹配一个行中所有以小写字母开头的字符串。

+号

匹配+号之前的字符出现 >=1 次

?号

在正则表达式中元字符 ? 标记在符号前面的字符为可选,即出现 0 或 1 次。 例如,表达式 [T]?he 匹配字符串 he 和 The。

锚点^号和$号

在正则表达式中,想要匹配指定开头或结尾的字符串就要使用到锚点。^ 指定开头,$ 指定结尾
\b,\B表示单词边界和非单词边界

{}号

在正则表达式中 {} 是一个量词,常用来限定一个或一组字符可以重复出现的次数。
例如, 表达式 [0-9]{2,3} 匹配最少 2 位最多 3 位 09 的数字。我们可以省略第二个参数。 例如,[0-9]{2,} 匹配至少两位 09 的数字。如果逗号也省略掉则表示重复固定的次数。 例如,[0-9]{3} 匹配3位数字

()号

  1. 将多个字符作为一个整体来处理。如(ab)+
  2. 捕获组(Capturing Group)
    括号内匹配的内容会被“捕获”,可以在后续的替换或引用中使用。
    例如(\d+)-(\d+)
    匹配像 “123-456” 这样的字符串,会捕获两个组:
    第一个捕获组是 “123”
    第二个捕获组是 “456”
    你可以在替换时用 $1, $2(某些语言用 \1, \2)来引用它们。

怎么区分捕获还是作整体?
使用非捕获组(?:…)表示只匹配不捕获.比如第一个例子要写成 (?:ab)+

3.命名捕获组
是正则表达式中的一种高级写法,它不仅能捕获匹配的内容,还能给这个捕获组取一个名字,方便在后续代码中引用和使用,尤其适合多个组的情况
(?P<组名>模式)就可以不用$1,$2这种索引名字
(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})

命名捕获组在不同语言中支持方式略有不同:
Python / .NET:用 (?P…)
JavaScript / Java:用 (?…)
替换中引用:JS 中可以用 $,Python 用 \g

字符集[ ]

字符集也叫做字符类。 方括号用来指定一个字符集。
在方括号中的字符集直接列举不关心顺序。 例如,表达式[Tt]he 匹配 the 和 The。(即方括号里是字符的取值范围,这里只能取T,t,如果有字符串che的话则不会被匹配)

在方括号中使用连字符来指定字符集的范围。 [1-9],[a-z],也可以组合使用
[a-zA-Z\d]

字符补集 ^

一般来说 ^ 表示一个字符串的开头,但它用在一个方括号的开头的时候,它表示这个字符集是否定的。 例如,表达式[^c]ar 匹配一个后面跟着ar的除了c的任意字符。

预定义的字符集

alt text

逻辑预查

预查(Lookaround)是正则表达式中用来匹配前后环境,但不消耗字符本身的高级技巧。可以把它理解成:“看看前面或后面是不是某个东西,但我不真正匹配它”。

?=… 正向肯定预查

?=… 为正先行断言,表示后面必须跟着 … 才能匹配成功,但不包括在匹配结果里。
\d(?=px)匹配 后面跟着 “px” 的数字,只匹配数字本身

?!.. 正向否定预查

后面不能是 … 才能匹配(说白了就是正先行断言的反例)
\d(?!px)匹配后面不跟着 “px” 的数字,只匹配数字本身

?<= … 反向肯定预查

前面必须是 …,才能匹配当前位置的字符
(?<=(T|t)he\s)(fat|mat) 匹配 fat 和 mat,且其前跟着 The 或 the。

?<!.. 反向否定预查

前面不能是 … 才能匹配当前位置

标志

标志也叫模式修正符,因为它可以用来修改表达式的搜索结果。 这些标志可以任意的组合使用,它也是整个正则表达式的一部分。
i忽略大小写g全局搜索m多行修饰符
例如,表达式 /The/gi 表示在全局搜索 The,在后面的 i 将其条件修改为忽略大小写,则变成搜索 the 和 The,g 表示全局搜索,即(不仅仅返回第一个匹配的,而是返回全部)。 例如,表达式 /.(at)/g 表示搜索 任意字符(除了换行)+ at,并返回全部结果(默认)
表达式 /at(.)?$/gm 表示小写字符 a 后跟小写字符 t ,末尾可选除换行符外任意字符。根据 m
修饰符,现在表达式匹配每行的结尾。

贪婪匹配和懒惰匹配

通过在量词后添加 ?,如 *? 或 +?,你告诉正则表达式引擎匹配尽可能少的字符
对于字符串 “aaaaa”:

使用贪婪模式的表达式 a* 会匹配整个字符串 “aaaaa”。
使用非贪婪模式的表达式 a*? 会匹配空字符串,因为 *? 表示匹配尽可能少的字符,包括零个字符。

常用的组合技

.*匹配接下来无限长的任意字符,不包括回车
.*balabala.* 匹配前面随意后面随意中间balabala的字符
[\s\S]表示任意字符
手机号(中国) ^1[3-9]\d{9}$
邮箱地址 ^\[\w.-]+@[\w.-]+\.\w+$
身份证号(简版) ^\d{15}(\d{2}[0-9xX])?$
IP 地址 ^(\d{1,3}\.){3}\d{1,3}$
URL https?\://[\w./?=&%-]+
日期(yyyy-mm-dd) ^\d{4}-\d{2}-\d{2}$
HTML 标签 <[^>]+>

匹配unicode字符的分类和文字脚本

识别字符串中是否包含汉字、阿拉伯字母、日文等特定字符
匹配多语言文本中的特定部分(比如只提取英文或中文)
做国际化处理、Unicode 数据清洗等场景非常实用
\p{Category名}
\p{sc=Script名}
alt text
还要更多,看 https://www.regular-expressions.info/unicode.html#category

使用

主要利用捕获组内的内容可以被引用来使用

python

python使用捕获组r"(\d+)" 引用捕获内容r"\1" .group(1)

1
2
3
4
5
6
7
import re
re.match() #从字符串开头尝试匹配
re.search() #匹配任意位置的第一个
re.findall() #找到所有匹配项,返回列表
re.finditer() #返回可遍历的匹配对象迭代器
re.sub() #替换
re.split() #按正则分割字符串

字符串还是字符串列表?

re.search() 用 .group(n) 获取
re.match() 用 .group(n) 获取
re.findall() 返回字符串或元组的列表
re.finditer() 返回多个 Match 对象可遍历
match.groups() 返回捕获组的元组
re.sub() 用 \1 引用组替换

最常用的findall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
re.findall(pattern, text_not_covered) 的作用是在字符串 text_not_covered 中查找所有与 pattern (正则表达式模式) 匹配的、非重叠的子字符串。

它的返回值是一个 列表 (list),列表的内容取决于 pattern 中是否包含捕获组 (...):

如果 pattern 中没有捕获组:
findall 返回一个包含所有匹配到的子字符串的列表。
例如:如果 pattern 是 r"\d+" (匹配一个或多个数字),text_not_covered 是 "hello 123 world 456",那么 matches 将会是 ['123', '456']。

如果 pattern 中有一个捕获组:
findall 返回一个列表,其中包含该捕获组所捕获到的所有内容 (而不是整个匹配)。
例如:如果 pattern 是 r"(\w+)\s+world" (匹配一个或多个字母数字下划线,后面跟空格和"world",并捕获前面的单词),text_not_covered 是 "hello world, good world",那么 matches 将会是 ['hello', 'good'] (只捕获了括号里的内容)。

如果 pattern 中有多个捕获组:
findall 返回一个包含元组 (tuple) 的列表。每个元组包含对应一次匹配中,所有捕获组捕获到的内容,顺序与捕获组在模式中出现的顺序一致。
例如:如果 pattern 是 r"(\w+)\s+(\d+)" (匹配单词后跟空格和数字,并分别捕获),text_not_covered 是 "apple 10 banana 25",那么 matches 将会是 [('apple', '10'), ('banana', '25')]。
如果没有找到任何匹配:

findall 返回一个 空列表 []。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
text = "Price: $12.99"
match = re.search(r"\$(\d+)\.(\d+)", text)

print(match.group(0)) # "$12.99"(整个匹配)
print(match.group(1)) # "12"
print(match.group(2)) # "99"
print(match.groups()) # 返回元组:("12", "99")

re.findall(r"\d+", "I have 2 apples and 3 bananas")
# 返回 ['2', '3'] —— 没有括号就是字符串列表

re.findall(r"I have (\d+) (\w+)", "I have 2 apples and I have 3 bananas")
# 返回 [('2', 'apples'), ('3', 'bananas')] —— 有括号就是元组列表

for match in re.finditer(r"(\d+)", "A1 B22 C333"):
print(match.group(1)) # 打印捕获的数字
print(match.start(), match.end()) # 可以获得位置信息

re.sub(r"(\d+)", r"<\1>", "Item 42 costs 99")
# => "Item <42> costs <99>"
#\1相当于group(1)
#\g<0>相当于group(0)
#匹配前后内容 自定义函数 + match.start() / end()

javascript

创建正则表达式对象

1
2
3
4
5
// 方式一:直接字面量
const regex = /\d+/;

// 方式二:RegExp 构造函数
const regex = new RegExp("\\d+");

构造了函数可以动态输入或者拼接规则.

为什么要动态处理?
  1. 关键词来自用户、接口、配置等变量

    1
    2
    const keyword = getUserInput();  // 用户输入
    const regex = new RegExp(keyword, "i");//i是不关注大小写

    根本无法预知用户会输入什么,所以没办法事先写死。

  2. 关键词是动态组合出来的

    1
    2
    3
    4

    const filters = ["jpg", "png", "svg"];//扩展名可能来自数据库、配置项、下拉选择……
    const pattern = `\\.(${filters.join("|")})$`;
    const regex = new RegExp(pattern, "i");

    静态根本没法覆盖全部可能。

  3. 需要动态拼接某段正则规则
    比如根据选项拼组合:

    1
    2
    3
    4
    5
    6
    let rule = "^";
    if (allowLetters) rule += "a-zA-Z";
    if (allowNumbers) rule += "0-9";
    rule = `[${rule}]+$`;

    const regex = new RegExp(rule);

    这个就没法靠 /…/ 写死。

  4. 用户输入中含有特殊字符,要先处理再用

    1
    2
    3
    4

    const userInput = "a+b";
    const escaped = userInput.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    const regex = new RegExp(escaped);

    不可能提前知道用户会输入 a+b、[abc] 还是 .*? 这些,静态写不了。

str.match() 匹配内容 返回字符串 or 数组
str.matchAll() 所有匹配 返回迭代器(每个是数组)
regex.exec() 执行一次匹配 返回数组(含捕获组)
regex.test() 是否匹配 返回布尔值
str.replace() 替换匹配项 返回字符串
str.split() 用正则分割 返回字符串数组
语法和python差不多,只是返回的一般都是数组,既有全部也有捕获组,还有位置

突然想起来python也可以动态创建正则表达式对象
使用 re.compile() + 字符串拼接

1
2
3
4
import re
pattern_str = r"\d{3}-" + r"\d{4}" # 动态拼接
regex = re.compile(pattern_str)
print(regex.match("123-4567")) # 匹配成功

讲了应该大多数可能会用到的东西
最后推荐网站 https://regex101.com/