正则
大约 6 分钟
正则
目录
QT的Pro文件
~=
操作符,使用指定的值替换符合正则表达式的值(例如SOURCE ~= s/\.cpp\b/.cxx
使用.cxx替换所有.cpp文件的扩展名)
QT的QRegExp
Qt的QRegExp
类是正则表达式的表示类,基于Perl的正则表达式语言,完全支持Unicode
组成:正则表达式由三部分组成
- 表达式(expressions)
- 量词(quantifiers)
- 断言(assertions)
表达式
- 最简单的表达式是一个字符
- 字符集举例
[AEIOU]
表示匹配所有的大写元音字母[^AEIOU]
表示匹配所有非元音字母,即辅音字母[a-z]
表示匹配所有的小写英文字母
量词
说明表达式出现的次数
举例
x[1,2]
表示x为1或2个[A-Za-z_]+[A-Za-z_0-9]*
匹配计算机语言的标识符
正则表达式的量词(
E
是表达式的意思)(这里的方括号好像要变成花括号才对,不知道为什么书上的是方括号)量词 含义 E?
匹配0次或1次,等同 E[0,1]
E+
匹配1次或多次,等同 E[1,]
E*
匹配0次或多次,等同 E[0,]
E[n]
匹配n次 E[n,]
匹配至少n次 E[,m]
匹配最多m次,等同 E[0,m]
E[n,m]
匹配n次到m次
断言
举例
using(?=E\s+namespace)
,匹配后面接 namespace 的 usingusing(?!E\s+namespace)
,匹配后面不接 namespace 的 usingusing\s+namespace
,匹配 using namespace
正则表达式的断言
符号 含义 ^
字符串开头进行匹配 $
字符串结尾进行匹配 \b
单词边界 \B
非单词边界 (?=E)
表达式后紧随E才匹配 (?!E)
表达式后不跟随E才匹配
MySQL
WHERE last_name REGEXP 'field' --正则表达式
-- 正则表达式,组合特殊字符建立复杂筛选
'field'包含,'^field'以开头,'field$'以结尾
'aa|bb'并行,'[abcd]e'单字符并行,'[a-d]e'并行优化
JS
RegExp 提供正则表达式的模式匹配
Python (重复,修改时要连同Python笔记一起修改)
字符串方法
只需简单的操作时,字符串方法最好用,因为它们易读,又容易调试:
>>> 'tea for too'.replace('too', 'two')
'tea for two'
re 基本使用
re 模块为高级字符串处理提供了正则表达式工具。对于复杂的匹配和处理,正则表达式提供了简洁、优化的解决方案:
findall、sub
>>> import re
>>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest') # 匹配
['foot', 'fell', 'fastest']
>>> re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat') # 替换。注意sub()方法替换字符串的时候如果不匹配,就会返回原字符串。很容易出错
'cat in the hat'
其他
详见文档
re.search(pattern, string, flags=0)
# 扫描整个 字符串 找到匹配样式的第一个位置,并返回一个相应的 匹配对象。如果没有匹配,就返回一个 None ; 注意这和找到一个零长度匹配是不同的。
re.match(pattern, string, flags=0)
# 如果 string 开始的0或者多个字符匹配到了正则表达式样式,就返回一个相应的 匹配对象 。 如果没有匹配,就返回 None ;注意它跟零长度匹配是不同的。
re.sub(pattern, repl, string, count=0, flags=0)
# 返回通过使用 repl 替换在 string 最左边非重叠出现的 pattern 而获得的字符串。 如果样式没有找到,则不加改变地返回 string。 repl 可以是字符串或函数;如为字符串,则其中任何反斜杠转义序列都会被处理。 也就是说,\n 会被转换为一个换行符,\r 会被转换为一个回车符,依此类推。 未知的 ASCII 字符转义序列保留在未来使用,会被当作错误来处理。 其他未知转义序列例如 \& 会保持原样。 向后引用像是 \6 会用样式中第 6 组所匹配到的子字符串来替换。
rearch和match的区别
# match()和search()都只匹配一个结果,但是match()是从字符串的开头开始匹配的,如果匹配的字符不是在开头处,那么它将会报错,匹配成功返回结果,没有返回None。而search()是从头开始匹配,匹配整一个字符串得出结果
import re
result1 = re.match('li', 'liadadafbba').group()
result2 = re.match('li', 'addadlidadaf')
print(result1, result2) # li None
import re
# result1 = re.match('li', 'liadadafbba')
result2 = re.match('li', 'addadlidadaf').group()
print(result2) # 报错
import re
result1 = re.search('li', 'liadadafbba').group()
result2 = re.search('li', 'addadlidadaf').group()
print(result1, result2) # li li
取子串(坑点)
坑的地方在于:序列0是完整匹配(默认是0),序列1才是第一个,非常反直觉......
>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m.group(0) # The entire match
'Isaac Newton'
>>> m.group(1) # The first parenthesized subgroup.
'Isaac'
>>> m.group(2) # The second parenthesized subgroup.
'Newton'
>>> m.group(1, 2) # Multiple arguments give us a tuple.
('Isaac', 'Newton')
不过话说其他程序中的正则也是,例如QT C++的 <QRegularExpression>
模块
QRegularExpression re(R"(^OnGetWebViewPageFinish:(.+?/log)$)");
QString line = in.readLine();
QRegularExpressionMatch match = re.match(line);
if(match.hasMatch())
{
qDebug()<<"WishUrl1: "<<match.captured(1); // 序列1才是第一个匹配项
return match.captured(1);
}
实战 - 匹配并替换内容
import re
pattern = r"元" # 模式字符串
str1 = "75元、89元" # 匹配字符串
replace = re.sub(pattern, "美元", str1)
print(str1) # "75美元、89美元"
在线工具
https://tool.oschina.net/regex
之前写过的一些正则参考
Obsidian插件 —— 自制
// 4. 切换鼠标所在行的复选框状态
this.addCommand({
id: 'test',
name: '切换鼠标所在行的复选框状态',
editorCallback: (editor: Editor, view: MarkdownView) => {
let s1 :string = editor.getLine(editor.getCursor().line) // 获取序列行的内容
let r1 = new RegExp(`^\\s*- \\[[( )]\\](.*)`)
let r2 = new RegExp(`^\\s*- \\[[(*+-x)]\\](.*)`)
if (s1.match(r1)) {
s1 = s1.replace("[ ]","[*]") // 这里写得比较简陋,会有bug,比如- [ ] [ ]
let ss = s1.match(r1)
}
else if (s1.match(r2)) {
s1 = s1.replace("[*]","[ ]")
s1 = s1.replace(r1, "d")
}
editor.setLine(editor.getCursor().line, s1)
}
});
Obsidian插件 —— Chat View
// 正则匹配
class ChatPatterns {
static readonly message = /(^>|<|\^)/; // 发送消息,正则:>或<开头
static readonly delimiter = /.../; // 省略消息,正则:省略号
static readonly comment = /^#/; // 全局消息,正则:#开头
static readonly colors = /\[(.*?)\]/; // 颜色设置,正则:[]包围,例如[Albus Dumbledore=teal, Minerva McGonagall=pink]
static readonly format = /{(.*?)}/; // 格式设置,正则:{}包围,例如{mw=90,mode=minimal}
static readonly joined = RegExp([this.message, this.delimiter, this.colors, this.comment, this.format]
.map((pattern) => pattern.source)
.join("|")); // 不名正则?
static readonly voice = /<v\s+([^>]+)>([^<]+)<\/v>/; // chat-webvtt模式下的对话检测
static readonly qq_msg = /(.*?)\s[0-2][0-9]:[0-6][0-9]:[0-6][0-9]/;
static readonly qq_chehui = /(.*?)撤回了一条消息/;
}
一些总结
双斜杆问题
- 使用单斜杠
- 在线工具中,
\s
就是空格,\\
就是反斜杠
- 在线工具中,
- 使用双斜杠
- JS
加强
(?😃 符号
const prefixPatternInMD = /^(?:>\s*)?-tx-\n/; // -tx-标识的识别,> -tx-亦可。正则中(?:)用於标记该匹配组不应被捕获
特殊
肯定式向前查找
匹配字符序列Start后跟一个空格和Test字符序列(不区分大小写)
正则模式:Start(?= Test)
匹配字符序列some,如果在同一句子中还存在字符序列some
正则模式:some(?=.*some.*)
否定式向前查找
匹配字符序列Start后面不存在test字符序列
正则模式:Start (?!test)
匹配Start 后面不存在test的行
正则模式: ^.*Start((?!test).)*$
肯定式向后查找
匹配前面有"rt"的字符序列Test
正则模式:(?<=rt )Test
否定式向后查找
匹配前面没有"rt "的字符序列Test
正则模式:(?<!rt) Test