Regexp入门
这一个文件夹里regexp才是大头,所以wildcard我们就较为简略的提两嘴带过了。Vscode中的查找功能内置了正则表达式模式的开关,Ctrl+F 后搜索框最右侧的即为开关。
另外,Python 中的 re 模块就是专门写给正则表达式的。详见Python - Re。
修饰符 Expression Flags
正则表达式一般由两斜杠 // 包裹起来,后面接上若干个修饰符。形如 /regexp/g,下面罗列一些修饰符:
- g:global,表示全局匹配
- i:case insensitive,表示大小写不敏感
- m:multiline,表示多行匹配
- s:single line,表示单行匹配
- u:unicode,表示支持unicode字符
- y:sticky,我也不知道啥意思
下面会介绍一系列保留字,如果需要匹配这些保留字,你需要一个反斜杠表示转义。
. 任意字符
. 在正则表达式中表示除了换行符之外的任意单个字符
[] 字符集合
和Nutrimatic一样,[abc] 表示一个字符集合,它会从中括号中“挑选一个字母“出来分别匹配。字符集合内的保留字全部失效,均被识别为待匹配字符。
- 字符范围
和通配符里面一样,- 在中括号内表示范围,且这个范围支持ASCII码范围内的所有字符。例如 [A-z] 就表示ASCII码从 A 到 Z 的所有字符。
^ 范围取反
和通配符里面一样,^ 在中括号开头时表示范围取反,例如 [^A-Za-z] 就表示除了字母以外的所有字符。
预定义字符类
如果你想匹配特定范围内的字符,除了上面的中括号,regexp还做了一些预制菜,但是需要转义一下。例如:
- \d:digit,表示数字
- \D:non-digit,表示非数字
- \w:所有的数字、字母或下划线
- \W:对上面的范围取反,包括空白,制表,换行…
- \s:表示空白字符,例如空格,制表…
^ 匹配一行开头
使用这个的时候需要开启多行匹配,即修饰符 m,不过在Vscode中这是不需要的,它默认多行匹配。例如 \^a\gm 就表示每一行开头是 a 的。
另外,需要明确的是 行开头 在regexp中被认为是一个实际存在的字符。也就是说,你可以replace掉行开头,从而在每一行开头都添加一个指定字符串,只不过会产生一个新的行开头。
$ 匹配一行结尾
与 ^ 相对,$ 表示每一行的结尾。这里不做过多赘述。
\b 单词边界
\b for \border,表示单词的边界。他与匹配字符的相对位置可以表示字符出现在单词的位置。或者你可以把他理解为空格?大多数情况下是这样的,只不过regexp不认为这是个空格,而是一个单词的开头或是结尾——它和上面的 行开头 和 行结尾 一样是零宽的。
例如 regexp very fun 中 \br 就可以搜到 |r|egexp 而搜不到 very。
\B 则表示非单词边界,大写字母一般都表示其小写字母所代表的字符类的
量词 Quantifier
量词顾名思义,即表示需要重复一定次数的匹配。
- + 表示前一个字符或字符块重复一次或多次
- * 表示前一个字符或字符块重复零次或多次
- ? 表示前一个字符或字符块重复零次或一次
{n}表示指定的重复次数,{n,m}可以表示一个范围,m置空代表n个及以上
[!attention] 贪婪匹配 贪婪匹配指字符串在满足条件后仍会继续向下寻找,尽可能多的匹配,直到条件不满足。 例如
at{3,}会匹配attt和atttttt而不是只匹配三个t就停下。 如果需要启用非贪婪模式,只需要在{3,}后面加一个问号,即{3,}?
| 多条件匹配
| 两边可以连接两个条件,只有同时满足两个条件的字符串会被匹配到。例如 ^a|a$ 会匹配到 a 和 aba。
分组与替换时的引用
有时候我们需要对一块字符进行更复杂的操作,例如将所有的日期进行格式上的统一,这就需要分组了。
分组
分组可以将多个字符视为一个字符来处理,符合直觉的——我们用小括号来进行分组。
- 例如
(at)*就可以匹配at和atat (a|b|c)abc这种用法也是允许的
引用
在分组之后,我们可以对分组的内容进行引用。例如我们对匹配的年份进行分组:
/(\d{4})[-/_]?(\d{1,2})[-/_]?(\d{1,2})/gm
2025-4-12
2025/5/12
2025_6_13
2025711
这时候,按照顺序,2025 被分到了 Group 1,4、5、6 被分到了每一个匹配串的 Group 2…在替换时,我们用 $n 来表示第 n 组的内容,所以我们可以用 $2 $3 $1 来将上面的字符串转化为
4_12_2025
5_12_2025
6_13_2025
7_11_2025
替换内的分组调用我们使用 $n,匹配内的分组应用我们使用 \n,例如 ([a-z])[a-z]*\1 就可以匹配首位字母相同的字符串。
非捕获分组
有时候我们只需要分组的功能,但不需要捕获,那么 (?:内容) 就可以实现。这个方法的好处在于减少内存占用,有一些大段的分组内内容就不会被记录到内存里面占空间。
分组命名
(?P<name>...) 表示对这个分组进行命名,在之后替换时你可以用这个命名来代替这个组别。你会在 Python - Re模块 中见到它的用法。
前瞻与后顾 lookahead and lookbehind
前瞻后顾功能可以让我们匹配到这些字符前面或者后面的内容,而非字符本身。
正向前瞻
正向前瞻的格式为 希望匹配内容(?=字符本身)。
\$(?=\d+)就可以匹配到$200、$100的$
负向前瞻
正向前瞻的格式为 希望匹配内容(?!字符)。其中感叹号表示取反,即不符合 字符 的字符串。
\$(?=\d+)可以匹配到$abc、$pdf的$
正向后顾 / 负向后顾
正向后顾的格式为 (?<=字符本身)希望匹配内容,负向后顾的格式为 (?<!字符)希望匹配内容。使用方法同理。
不同版本的regex也有不同的用法,具体还是去看手册,这里我们只提到了一些通用的用法。总的来说,regex可以打打方便字符的查找和替换,是一个相当方便的工具!