编辑
2024-08-14
Python
0
请注意,本文编写于 247 天前,最后修改于 247 天前,其中某些信息可能已经过时。

目录

前言
简单的正则表达式语法
匹配
通配符
字符集
字符的重复
捕获组
分支
位置匹配
转义
符号转义
特殊转义
断言
正向先行断言
正向后行断言
反向先行断言
反向后行断言
贪婪匹配与懒惰匹配

前言

由于 神里绫华的狗 上的内容都是使用Latex编写的,无论是发表在本站还是知乎等平台,都需要将Latex转为markdown语法。由于简单情况下两者语法接近,并且markdown也支持使用latex的公式,这个转换无非就是做一些字符串的替换,估摸着应该也不是很难的样子。

但是实际写起来并不是那么简单。比如要把Latex的公式符号\[替换为$$,同时也会影响换行标记

latex
\\[1em]

要实现这样的判断,简单的字符串匹配是做不到的,需要使用正则表达式

简单的正则表达式语法

在开始之前,首先需要插一嘴,在python中,有一种字符串是这样写的

python
r'abcd\n'

前面加一个r意为后面的字符串就是它字面意义上的意思,不进行转义。像这里\n就不是换行符的意思,它真的是一个斜杠\和一个n。由于正则表达式是需要转义的,如果不这样书写字符串就要转义两次,想想都蛋疼。

匹配

最简单的匹配当然是之间匹配字符串本身

python
r'abcd'

这就是匹配abcd,与普通的字符串匹配没什么两样。

通配符

正则表达式中存在通配符.,它代表着任意的单个字符。注意它只是一个字符,并不像平常用的*一样可以代表任意字符串。比如

'abcde'

正则表达式r'.'的匹配结果将会是一个列表

python
['a','b','c','d','e']

字符集

字符集用中括号[]表示。字符集也只是代表一个字符,并不是字符串。字符集的作用是匹配中括号中的任意一个字符,如

python
‘abc adc aec’

用正则表达式r'a[be]c'匹配的结果就是

python
['abc','aec']

在字符集的开头加上^构成否定字符集[^],即匹配不是括号中的任意一个字符,如

python
'abc adc aec'

用正则表达式r'a[^be]c'匹配的结果就是

python
'adc'

字符集还可以指定范围,如

python
r'[a-f]'

等效于r'[abcdef]'。数字也适用

python
r'[1-5]'

它等价于r'[12345]'。字符集可以组合使用,如

python
r'[a-f1-5xyz]'

它匹配满足以下三种情况的一个字符:在a-f中,或是在1-5中,或是x,y,z中的一个

字符的重复

前面提到的字符集只能匹配一个字符,将单个字符组成字符串需要用到重复

简单的重复是*,+,?

  • *意为前面的字符可以出现0次或更多次
  • +意为前面的字符串可以出现1次或更多次
  • ?意为前面的字符串可以出现0次或1次

如正则表达式go*gle可以匹配gglego+gle不可以

高级一些的方法是用大括号{}。当大括号中只有一个数时它代表着前面字符出现的数目,如

python
r'go{2}gle`

就等价于r'google

当大括号中有两个数字时,就代表着前面字符出现的数目范围

python
r'go{2,4}gle`

代表着中间的字母o出现2次,3次或4次,所能匹配的全部字符串就是

google gooogle goooogle

如果不填,则代表对次数没有要求,如

python
r'go{1,}gle`

要求中间的o最少出现一次,最多出现任意次,中间爱有几个就有几个,这等效于go+gle

捕获组

捕获组由括号组成(),括号中是一个正则表达式,就像这样

python
r'(abc)`

为什么需要捕获组?引入捕获组的一大目的是引用!如

python
r'(.+)-\1`

这个正则表达式中,\1代表着第一个捕获组匹配上的内容,因而这个正则表达式匹配的结果形为

water-water gbwater-gbwater ...

引入捕获组的另一个目的是便于表示字符串。正则表达式中的内容都是匹配字符的,如果要匹配字符串需要在正则表达式中嵌套正则表达式,如

python
r'(.*)(water)'

虽然它的作用与.*water完全相同,但是如此一来,该正则表达式就从匹配字符变为了匹配两个字符串。这样正则表达式就可以模块化设计了。如果仅仅是为了模块化,并不需要捕获,可以像下面这样写,称为非捕获组

python
r'(?:abc)`

该捕获组匹配的字符串不可被引用,引用时不计入编号

分支

一个正则表达式有多个分支,用竖线|表示。如

python
r'{a-z}+|{0-9}+|{A-Z}+`

匹配三种字符串:完全由小写字母a-z组成的,或完全由数字0-9组成的,或完全由大写字母A-Z组成的

结合捕获组的模块化,可以设计复杂的正则表达式,如

python
r'(神里绫华|流萤酱|芙芙)的狗`

将会匹配

python
['神里绫华的狗','流萤酱的狗','芙芙的狗']

位置匹配

利用^可以匹配行首,如下面的正则表达式

python
r'^(神里绫华|芙芙)的狗'

对于字符串

神里绫华的狗也是芙芙的狗

将只会匹配到神里绫华的狗

同样利用$可以匹配行尾。如果上面的正则表达式这样写

python
r'(神里绫华|芙芙)的狗$'

对于同样的字符串,就只能匹配到芙芙的狗

转义

符号转义

对于如+*|等被正则表达式占用的符号,需要使用时用反斜杠\进行转义。特别地,想要打出\也需要转义

python
r'\\'

特殊转义

一些特殊转义可以被列举如下

  • \w代表一个可以组成单词的字符,包括大写字母A-Z,小写字母a-z和下划线_
  • \W代表一个不能组成单词的字符,就是\w字符集的补集
  • \d代表一个数字字符
  • \D代表一个不是数字的字符
  • \s代表一个空白字符,包括空格 ,制表符\t和换行符\n
  • \S代表一个非空白字符,就是补集

断言

有时我们会想让字符串前面或是后面具有一些字符串,但是又不想这些字符串被匹配进去。如

我是神里绫华的狗

我希望提取出这是谁的狗,但是不想一并提取出我是的狗,就需要使用断言

断言分为正向断言和负向断言,先行断言和后行断言

正向先行断言

正向先行断言为(?=),一个例子为

python
r'(?=我是)(.+)'

它将匹配以我是开头的字符串,如我是神里绫华的狗就会匹配到神里绫华的狗

正向后行断言

正向后行断言为(?<=),一个例子为

python
r'(.+)(?<=的狗`

它将匹配以的狗结尾的字符串,如我是神里绫华的狗就会匹配到我是神里绫华

反向先行断言

反向先行断言为(?!),一个例子为

python
r'(?!我不是)神里绫华(.+)'

它将匹配不以我不是开头的字符串,如我不是神里绫华的狗将不会被匹配到

反向后行断言

反向先后断言为(?<!),一个例子为

python
r'(.+)芙芙(?<!嘿嘿嘿)'

它将匹配不以嘿嘿嘿结尾的字符串,如嘿嘿芙芙嘿嘿嘿将不会被匹配到

贪婪匹配与懒惰匹配

正则表达式默认为贪婪匹配,即尽可能匹配最多的内容。若需要切换为懒惰匹配,可以在字符前加上?,这样前面的匹配内容就会在该字符第一次被匹配到是停止扩展。如

芙芙嘿嘿嘿嘿嘿

在使用正则表达式

python
r'(.*)嘿

将会匹配到整个字符串;而使用下面这个正则表达式

python
r'(.*)?嘿

就只会匹配到芙芙

本文作者:GBwater

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!