RegEx基础
正则表达式,或称为 RegEx(Regular Expression),是一系列字符,形成一个搜索模式。
正则表达式可以用来检查字符串是否包含指定的搜索模式。
常用符号
字符匹配
普通字符:字母、数字和普通符号(如 a、1、!)在正则表达式中都代表它们本身。
转义字符:”\“ 取消元字符的特殊意义,或表示一个预定义字符类。
. :匹配任意单个字符(除换行符)。
字符类
1 | [abc]:匹配 a、b 或 c 中的任意一个字符。 |
示例1:[^0-9] 匹配任何非数字字符。示例:[^0-9] 匹配任何非数字字符。
示例2:b[aeiou]t 匹配 “bat”、”bet”、”bit”、”bot”、”but”。
预定义字符类:
1 | \d 匹配任何十进制数;它相当于类 [0-9]。 |
量词
1 | *:匹配前一个字符 0 次或多次。 |
注:”m,n”之间不能有空格,n的缺省值是无穷
边界
边界用于指定匹配的位置
1 | ^:匹配字符串的开始。 |
示例1:^hello 只能匹配以 “hello” 开头的字符串
示例2:world$ 只能匹配以 “world” 结尾的字符串。
示例3:\bword\b 只匹配独立的 “word” 而不是 “sword”。
示例4:\Bword\B 匹配 “sword” 中的 “word”。
组和引用
捕获组:用 (…) 将部分表达式括起来,用于将匹配的内容提取出来。
示例1:(abc) 会捕获字符串中的 “abc”。
引用捕获组:在正则表达式中,可以使用 \1、\2 等来引用之前的捕获组。
示例2:(\d)\1 匹配重复的数字,如 “11”、”22”。
管道符
|:表示“或”操作。”|”的优先级非常低,是为了当你有多字符串要选择时能适当地运行。
示例
匹配 URL
1 | https?://([\w.-]+\.)+[a-zA-Z]{2,6}(/[\w&%?./-]*)? |
匹配邮箱地址
1 | [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,} |
匹配 IPv4 地址
1 | ^(25[0-5]|2[0-4]\d|1\d{2}|\d{1,2})(\.(25[0-5]|2[0-4]\d|1\d{2}|\d{1,2})){3}$ |
匹配电话号码(+86)
1 | ^1[3-9]\d{9}$ |
匹配中国大陆居民身份证ID
1 | ^\d{6}(18|19|20)?\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}(\d|X|x)?$ |
提取页面中的图片链接
1 | <img\s+[^>]*src="([^"]+)" |
提取json数据
1 | <script[^>]*>.*?({.*?}).*?</script> |
进阶(待续)
零宽断言
零宽断言(Zero-width assertions)是一种特殊正则构造,在不消耗字符的情况下进行条件匹配。
零宽断言主要有四种类型:
Positive Lookahead
用于匹配条件满足后的位置
基本语法:
1 | (?=xxx) |
示例:
1 | \w+(?=\d) # 匹配后面跟着一个数字的一个或多个字母数字 |
Negative Lookahead
用于匹配条件不满足后的位置
基本语法:
1 | (?!xxx) |
示例:
1 | \w+(?!\d) # 匹配后面不能跟着一个数字的一个或多个字母数字 |
Positive Lookbehind
匹配条件前面的位置
基本语法:
1 | (?<=xxx) |
示例:
1 | (?<=@)\w+ # 匹配前面必须带@的一个或多个字母数字 |
Negative Lookbehind
匹配条件不在前面的位置
基本语法:
1 | (?<!xxx) |
示例:
1 | (?<!@)\w+ # 匹配前面不能有@的一个或多个字母数字 |
命名捕获组
命名捕获组是正则表达式中的一种增强特性,用于将匹配的内容以命名进行捕获。和传统捕获组的不同在于:命名捕获组通过给组指定一个名称,使提取数据时更加直观。
基本语法:
1 | (?<name>...) |
示例:解析URL(Python)
1 | import re |
条件匹配
条件匹配在正则中用于根据特定条件选择性地应用不同的匹配模式。条件匹配通常结合捕获组和特定语法来实现。
基本语法:
1 | (?(n)yes|no) |
示例1:匹配可选前缀 pre-
1 | (?(1)pre-|\b)([a-zA-Z]+) |
条件部分 (?(1)pre-|\b):
?(1):表示一个条件语句,检查第一个捕获组 1 是否匹配。
pre-:如果捕获组 1 匹配,则要求匹配前缀 pre-。
|:分隔符,表示条件的两种可能情况。
\b:如果捕获组 1 不匹配,则匹配一个单词边界。这里的 \b 确保了如果没有前缀 pre-,则后面的匹配部分可以单独存在,不与前面的字母相连。
主匹配部分 ([a-zA-Z]+)
示例2:日期格式
1 | (?:(?P<d>\d{1,2})/(?P<m>\d{1,2})/(?P<y>\d{4})|(?P<y2>\d{4})-(?P<m2>\d{1,2})-(?P<d2>\d{1,2})) |
d/m/y 格式
y-m-d 格式
回溯引用
回溯引用(Backreference)是一种在正则表达式中使用的特性,它允许你在模式中引用之前匹配的捕获组。
捕获组可以通过 \n 引用自己之前匹配的内容,n 为捕获组的编号或名称。
示例1:匹配重复出现的单词
1 | \b(\w+)\s+\1\b |
\b:表示单词边界。
(\w+):匹配一个或多个字母或数字,并将其捕获为组1。
\s+:匹配一个或多个空白字符。
\1:引用捕获组1,表示与之前匹配的单词相同。
示例2:匹配括号成对
1 | \((.*?)\)\(\1\) |
\( 和 \):匹配左括号和右括号。
(.*?):匹配任意字符(非贪婪模式),并将其捕获为组1。
\1:引用组1,确保两个括号中的内容相同。
嵌套组和递归
递归是指在正则表达式中调用自身的能力,允许正则表达式通过递归调用来匹配嵌套结构。
示例1:匹配括号成对
1 | \(([^()]|(?R))*\) |
\( 和 \):匹配左括号和右括号。
[^()]*:匹配任意不包含括号的字符。
(?R):表示递归匹配整个正则表达式,使得可以匹配嵌套的括号。
示例2: 匹配标签嵌套
1 | <([a-zA-Z][a-zA-Z0-9]*)>(?:[^<]+|(?R))*<\/\1> |
<([a-zA-Z][a-zA-Z0-9]*)>:匹配开始标签,并捕获标签名。
(?:[^<]+|(?R))*:匹配非标签内容或递归调用自身以匹配嵌套标签。
<\/\1>:匹配结束标签,\1 用于引用开始标签的名字。
模式修饰符
模式修饰符用于改变正则表达式的行为,通常在模式的开头添加。
常见的修饰符包括:
1 | i:忽略大小写。 |
示例:忽略大小写匹配“hello”:
1 | (?i)hello |
贪婪匹配与懒惰匹配
贪婪匹配和懒惰匹配是正则表达式中控制重复匹配行为的两种策略。
贪婪匹配
贪婪匹配是尽可能多地匹配字符
示例:匹配引号之间的所有内容
1 | ".*" |
懒惰匹配
贪婪匹配是通过在量词后添加 ? 来实现懒惰匹配,即尽可能少地匹配字符。
示例:匹配引号之间的最短内容
1 | ".*?" |
原子组
原子组通过使用 (?>xxx) 来定义,它将一个子模式标记为“原子”,表示该组的内容在匹配后不能被重新评估或回溯。
基本语法:
1 | (?>xxx) |
示例:
1 | (?>\d+)\d |
\d+:表示匹配一个或多个数字字符(0-9)。
\d:表示匹配一个数字字符。
模式注释
通过 (?x) 开启模式注释,可以在正则表达式中加入空格和注释,提升可读性。
此时,正则表达式中的空白符(空格、换行符等)会被忽略,除非它们在字符类 […] 内被转义。
示例1:匹配邮箱地址格式(通用)
1 | (?x) # 启用模式注释 |
示例2:匹配日期(Python:在 re.compile() 中使用 re.VERBOSE)
1 | import re |