/

正则表达式入门课

01 元字符

正则表达式 —— 字符串的规则。

元字符就是指那些在正则表达式中具有特殊意义的专用字符。

image

02 量词与贪婪

贪婪(Greedy) *:匹配最长。在贪婪量词模式下,正则表达式会尽可能长地去匹配符合规则的字符串,且会回溯。

preg_match_all("/a*/i", "aaabb", $matches);
var_dump($matches);

非贪婪(Reluctant) +?:匹配最短。在非贪婪量词模式下,正则表达式会匹配尽可能短的字符串。

ENV:Python3

import re
re.findall(r'a*', 'aaabb') # 贪婪模式
# ['aaa', '', '', '']
re.findall(r'a*?', 'aaabb') # 非贪婪模式
# ['', 'a', '', 'a', '', 'a', '', '', '']

re.findall(r'".+"', '"the little cat" is a toy, it lokks "a little bad"') # 贪婪模式
# ['"the little cat" is a toy, it lokks "a little bad"']
re.findall(r'".+?"', '"the little cat" is a toy, it lokks "a little bad"') # 贪婪模式
# ['"the little cat"', '"a little bad"']

独占模式(Possessive) ++:同贪婪一样匹配最长。不过在独占量词模式下,正则表达式尽可能长地去匹配字符串,一旦匹配不成功就会结束匹配而 不会回溯

# 回溯示例:
import re
re.findall(r'xy{1,3}z', 'xyyz') # 回溯
# ['xyyz']
# 正则 xy{1,3} 会尽可能长地去匹配到 xyyz,无法匹配 z,向前回溯 xyy
# 正则 z 匹配到剩下字符串 z
re.findall(r'xy{1,3}?z', 'xyyz') # 非贪婪
# ['xyyz']
# 正则 xy{1,3} 会尽可能短地去匹配到 xy
# 正则 z 匹配到字符串 y,无法匹配,向前回溯
# 正则 xy{1,3} 会尽可能短地去匹配 xyy
# 正则 z 匹配到剩下字符串 z
# 独占模式示例:
# pip install regex -i https://mirrors.aliyun.com/pypi/simple/
import regex
regex.findall(r'xy{1,3}+z', 'xyyz') # 独占
# ['xyyz']
# 正则 xy{1,3}+ 会尽可能长地去匹配到 xyy 并占用
# 正则 z 匹配到字符串 z
regex.findall(r'xy{1,3}+yz', 'xyyz') # 独占
# []
# 正则 xy{1,3}+ 会尽可能长地去匹配到 xyy 并占用
# 正则 yz 无法匹配到剩下字符串 z

03 分组与引用

import regex
# 不保存分组 (?:正则)
regex.sub(r'(\d{4})-(?:\d{2})-(\d{2})', r"年:\1 日:\2", '2023-03-01')
# '年:2023 日:01'

# 去除重复连续单词
regex.sub(r'(\w+)(\s\1)+', r"\1", 'the little cat cat is in the hat hat hat, we like it.')
# 'the little cat is in the hat, we like it.'

04 匹配模式

指改变元字符匹配行为。

不区分大小写模式(Case-Insensitive)(?模式标识) (?i)

import regex
regex.findall(r"(?i)cat", "cat Cat CAt")
# ['cat', 'Cat', 'CAt']
# https://regex101.com/r/3OUJda/1
# 二次重复时的大小写一致
((?i)cat) \1

点号通配模式(Dot All)(?s) 让英文的点 . 可以匹配上包括换行的任何字符。等价 [\s\S] [\d\D] [\w\W]

# https://regex101.com/r/zXtwLv/1
# 匹配包括换行符
(?s).+

多行匹配模式(Multiline)(?m) 使 ^$ 能匹配上每行的开头或结尾。

# 分行匹配
(?m)^cat|dog$

注释模式(Comment)(?#)

(\w+)(?#word) \1(?#word repeat again)

05 断言 Assertion

对要匹配的文本的位置也有一定的要求。只用于匹配位置,而不是文本内容本身,这种结构就是断言。

边界(Boundary)

import re
# 单词边界 \b
# tom -> jerry, tomorrow 不受影响
re.sub(r'\btom\b', 'jerry', "tom asked me if I would go fishing with him tomorrow.")
# 'jerry asked me if I would go fishing with him tomorrow.'

# 行的开始结束
# \A \z 不受模式影响
# \A -> ^, \z -> $
re.sub(r'\Atom', 'jerry', "tom asked me if I would go fishing with him tomorrow.")
# 环视 左尖括号代表看左边,没有尖括号是看右边,感叹号是非的意思
# (?<=Y) 左边是Y
# (?<=!Y) 左边不是Y
# (?=Y) 右边是Y
# (?!Y) 右边不是Y

re.findall(r'[1-9]\d{5}', "138001380002")
# ['138001', '380002']
re.findall(r'(?<!\d)[1-9]\d{5}(?!\d)', "138001380002")
# []
re.findall(r'(?<!\d)[1-9]\d{5}(?!\d)', "code138001code")
# ['138001']

# \b\w+\b -> (?<!\w)\w+(?!\w) -> (?<=\W)\w+(?=\W)
# https://regex101.com/r/PBEKxY/1

06 转义

import re
re.findall(r'\\d', 'abc\\d123d\\')