跳至主要內容

正则

LincZero大约 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 的 using
      • using(?!E\s+namespace),匹配后面不接 namespace 的 using
      • using\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 基本使用

reopen in new window 模块为高级字符串处理提供了正则表达式工具。对于复杂的匹配和处理,正则表达式提供了简洁、优化的解决方案:

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-亦可。正则中(?:)用於标记该匹配组不应被捕获

特殊

参考:【简书】正则表达式里”不包含”及一些特殊查找open in new window

肯定式向前查找

匹配字符序列Start后跟一个空格和Test字符序列(不区分大小写)
正则模式:Start(?= Test)
匹配字符序列some,如果在同一句子中还存在字符序列some
正则模式:some(?=.*some.*)

否定式向前查找

匹配字符序列Start后面不存在test字符序列
正则模式:Start (?!test)
匹配Start 后面不存在test的行
正则模式: ^.*Start((?!test).)*$

肯定式向后查找

匹配前面有"rt"的字符序列Test
正则模式:(?<=rt )Test

否定式向后查找

匹配前面没有"rt "的字符序列Test
正则模式:(?<!rt) Test