Skip to main content

Regular Expressions Cheatsheet 正则表达式小抄

假如有如下csv表格

order_id, fee_amount <br>
1, 1 <br>
2, 2 <br>
...

希望能够转换成下面的SQL UDPATE语句

UPDATE xx_tab SET fee_amount = 666 WHERE order_id = 1; <br>
UPDATE xx_tab SET fee_amount = 666 WHERE order_id = 2; <br>
...

可以使用正则在vscode替换、或写脚本快速完成

import re

csv_data = """order_id, fee_amount
1, 1
2, 2
3, 3"""

pattern = r"(\d+),\s*(\d+)"
replacement = r"UPDATE xx_tab SET fee_amount = 666 WHERE order_id = \1;"

sql_output = re.sub(pattern, replacement, csv_data)

print(sql_output)

捕获vs非捕获 Capturing VS non-capturing

特性捕获组 (…)非捕获组 (?:…)
定义方式使用圆括号,如 (abc)在圆括号内加入 ?:,如 (?:abc)
匹配功能分组并保存匹配结果,可用于后续引用仅分组,不保存匹配结果
组编号自动分配编号(\1、\2 等),保存于捕获组列表中不参与组编号,不会影响捕获组的编号
应用场景当需要提取匹配内容或者在正则中使用反向引用时使用仅需要逻辑分组,不需要提取数据时使用

捕获组用于存储匹配的子模式,以便后续引用。可以通过索引(位置捕获)或名称(命名捕获)访问。

位置捕获(索引访问)

import re

pattern = r'(\d{4})-(\d{2})-(\d{2})'
text = "2025-02-26"
match = re.match(pattern, text)

if match:
print(match.group(1)) # 输出 2025
print(match.group(2)) # 输出 02
print(match.group(3)) # 输出 26

命名捕获(名称访问)

命名捕获组允许使用有意义的名称代替编号,提高可读性和可维护性。

import re

pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
text = "2025-02-26"
match = re.match(pattern, text)

if match:
print(match.group("year")) # 输出 2025
print(match.group("month")) # 输出 02
print(match.group("day")) # 输出 26

反向引用(Backreferences)

反向引用用于在正则表达式内部引用先前匹配的捕获组。

  • 位置捕获使用 \n 进行反向引用(n 是捕获组编号)。
  • 命名捕获使用 (?P=name) 进行反向引用。

示例:匹配重复的单词

import re

pattern = r'\b(\w+) \1\b'
text = "hello hello world"
match = re.search(pattern, text)

if match:
print(match.group(1)) # 输出 hello

使用命名捕获的版本:

import re

pattern = r'\b(?P<word>\w+) (?P=word)\b'
text = "hello hello world"
match = re.search(pattern, text)

if match:
print(match.group("word")) # 输出 hello

案例分析

示例编号示例文本正则表达式匹配结果说明
1"hello hello"(\w+)\s+\1匹配 "hello hello"捕获一个单词,然后要求后面出现相同的单词。
2"<div>Content</div>"<(\w+)>.*?</\1>匹配整个 <div>Content</div>捕获 HTML 标签名(如 div),并确保结束标签与开始标签一致。
3"aba"^(\w).\1$匹配 "aba"三字符字符串,第一个和第三个字符必须相同。
4"abcabc"^(\w+)\1+$匹配 "abcabc"捕获一个子串(如 "abc"),并要求整个字符串由该子串重复构成。
5"test test"(?P<word>\w+)\s+(?P=word)匹配 "test test"使用命名捕获组,将匹配的单词命名为 word,然后通过 (?P=word) 引用。

零宽断言

零宽断言是正则表达式中的一种特殊工具,用于检测目标位置前后是否符合指定的模式,但不会捕获(capture)这些字符。根据检测方向及要求的不同,零宽断言分为以下四种类型:

断言类型语法说明示例说明
正向先行断言(?=pattern)匹配当前位置后面必须跟随指定模式(pattern)的情况\w+(?=\d):匹配一个单词,当且仅当其后紧跟数字时匹配成功
负向先行断言(?!pattern)匹配当前位置后面不能跟随指定模式(pattern)的情况foo(?!bar):匹配“foo”,但要求其后不能跟“bar”
正向后顾断言(?<=pattern)匹配当前位置前面必须以指定模式(pattern)结束的情况(?<=\$)\d+:匹配一串数字,要求其前面紧跟一个美元符号 $
负向后顾断言(?<!pattern)匹配当前位置前面不能以指定模式(pattern)结束的情况(?<!\$)\d+:匹配一串数字,要求其前面没有美元符号 $

贪婪

\说明语法示例
贪婪尽可能重复限定词x{1,100}\d{2,6} 捕获 12342222222222 中的123422
非贪婪不重复限定词,在数量后后加个问号x{1,100}?\d{2,6}? 捕获 12342222222222 中的 12

RegexFlag

正则表达式描述反义表达式反义描述
\w匹配字母、数字、下划线[0-9a-zA-Z_]\W匹配非字母、非数字、非下划线
\s匹配空白字符(空格、\t\n\r 等)\S匹配非空白字符
\d匹配数字0-9\D匹配非数字
\b匹配单词边界(单词开头或结尾)\B匹配非单词边界
[x]匹配字符 x[^x]匹配除 x 以外的字符
\n匹配换行符————
\g<name>\g<number>引用命名或编号的捕获组————
^匹配行开头————
$匹配行结尾————
(?i)启用大小写不敏感匹配————
/a(?i)a/g行内大小写不敏感匹配(匹配 aAaa,但不匹配 ab————
\L(\w)转换捕获的字符为小写\U(\w)转换捕获的字符为大写

与或非

正则中只有或,与和非可以用零宽断言(前瞻、负前瞻)来实现。

操作方法主要技巧适用场景
与(AND)(?=.*A)(?=.*B).*Lookahead(前瞻)需要匹配多个条件同时存在
或(OR)**``(管道符)**`A
非(NOT)(?!A).*[^...]负向前瞻 (?!A)、排除字符 [^...]需要排除某些模式

vscode 替换案例

不替换第一个捕获的值

  1. 搜索 (.*)
  2. 使用 $1 替换

保留前后的捕获值

  1. 搜索 (Manager)(\s)(\w.*Manager,)
  2. 使用 $1 handler.$3 替换

参考

  1. regex101
  2. 你是如何学会正则表达式的? https://www.zhihu.com/question/48219401/answer/742444326