由于 神里绫华的狗 上的内容都是使用Latex
编写的,无论是发表在本站还是知乎等平台,都需要将Latex
转为markdown
语法。由于简单情况下两者语法接近,并且markdown
也支持使用latex
的公式,这个转换无非就是做一些字符串的替换,估摸着应该也不是很难的样子。
但是实际写起来并不是那么简单。比如要把Latex
的公式符号\[
替换为$$
,同时也会影响换行标记
latex\\[1em]
要实现这样的判断,简单的字符串匹配是做不到的,需要使用正则表达式。
在开始之前,首先需要插一嘴,在python中,有一种字符串是这样写的
pythonr'abcd\n'
前面加一个r
意为后面的字符串就是它字面意义上的意思,不进行转义。像这里\n
就不是换行符的意思,它真的是一个斜杠\
和一个n
。由于正则表达式是需要转义的,如果不这样书写字符串就要转义两次,想想都蛋疼。
最简单的匹配当然是之间匹配字符串本身
pythonr'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'
字符集还可以指定范围,如
pythonr'[a-f]'
等效于r'[abcdef]'
。数字也适用
pythonr'[1-5]'
它等价于r'[12345]'
。字符集可以组合使用,如
pythonr'[a-f1-5xyz]'
它匹配满足以下三种情况的一个字符:在a-f
中,或是在1-5
中,或是x,y,z
中的一个
前面提到的字符集只能匹配一个字符,将单个字符组成字符串需要用到重复
简单的重复是*
,+
,?
。
*
意为前面的字符可以出现0次或更多次+
意为前面的字符串可以出现1次或更多次?
意为前面的字符串可以出现0次或1次如正则表达式go*gle
可以匹配ggle
而go+gle
不可以
高级一些的方法是用大括号{}
。当大括号中只有一个数时它代表着前面字符出现的数目,如
pythonr'go{2}gle`
就等价于r'google
当大括号中有两个数字时,就代表着前面字符出现的数目范围
pythonr'go{2,4}gle`
代表着中间的字母o
出现2次,3次或4次,所能匹配的全部字符串就是
google gooogle goooogle
如果不填,则代表对次数没有要求,如
pythonr'go{1,}gle`
要求中间的o
最少出现一次,最多出现任意次,中间爱有几个就有几个,这等效于go+gle
捕获组由括号组成()
,括号中是一个正则表达式,就像这样
pythonr'(abc)`
为什么需要捕获组?引入捕获组的一大目的是引用!如
pythonr'(.+)-\1`
这个正则表达式中,\1
代表着第一个捕获组匹配上的内容,因而这个正则表达式匹配的结果形为
water-water gbwater-gbwater ...
引入捕获组的另一个目的是便于表示字符串。正则表达式中的内容都是匹配字符的,如果要匹配字符串需要在正则表达式中嵌套正则表达式,如
pythonr'(.*)(water)'
虽然它的作用与.*water
完全相同,但是如此一来,该正则表达式就从匹配字符变为了匹配两个字符串。这样正则表达式就可以模块化设计了。如果仅仅是为了模块化,并不需要捕获,可以像下面这样写,称为非捕获组
pythonr'(?:abc)`
该捕获组匹配的字符串不可被引用,引用时不计入编号
一个正则表达式有多个分支,用竖线|
表示。如
pythonr'{a-z}+|{0-9}+|{A-Z}+`
匹配三种字符串:完全由小写字母a-z
组成的,或完全由数字0-9
组成的,或完全由大写字母A-Z
组成的
结合捕获组的模块化,可以设计复杂的正则表达式,如
pythonr'(神里绫华|流萤酱|芙芙)的狗`
将会匹配
python['神里绫华的狗','流萤酱的狗','芙芙的狗']
利用^
可以匹配行首,如下面的正则表达式
pythonr'^(神里绫华|芙芙)的狗'
对于字符串
神里绫华的狗也是芙芙的狗
将只会匹配到神里绫华的狗
同样利用$
可以匹配行尾。如果上面的正则表达式这样写
pythonr'(神里绫华|芙芙)的狗$'
对于同样的字符串,就只能匹配到芙芙的狗
对于如+
,*
,|
等被正则表达式占用的符号,需要使用时用反斜杠\
进行转义。特别地,想要打出\
也需要转义
pythonr'\\'
一些特殊转义可以被列举如下
\w
代表一个可以组成单词的字符,包括大写字母A-Z
,小写字母a-z
和下划线_
\W
代表一个不能组成单词的字符,就是\w
字符集的补集\d
代表一个数字字符\D
代表一个不是数字的字符\s
代表一个空白字符,包括空格
,制表符\t
和换行符\n
\S
代表一个非空白字符,就是补集有时我们会想让字符串前面或是后面具有一些字符串,但是又不想这些字符串被匹配进去。如
我是神里绫华的狗
我希望提取出这是谁的狗,但是不想一并提取出我是
和的狗
,就需要使用断言
断言分为正向断言和负向断言,先行断言和后行断言
正向先行断言为(?=)
,一个例子为
pythonr'(?=我是)(.+)'
它将匹配以我是
开头的字符串,如我是神里绫华的狗
就会匹配到神里绫华的狗
正向后行断言为(?<=)
,一个例子为
pythonr'(.+)(?<=的狗`
它将匹配以的狗
结尾的字符串,如我是神里绫华的狗
就会匹配到我是神里绫华
反向先行断言为(?!)
,一个例子为
pythonr'(?!我不是)神里绫华(.+)'
它将匹配不以我不是
开头的字符串,如我不是神里绫华的狗
将不会被匹配到
反向先后断言为(?<!)
,一个例子为
pythonr'(.+)芙芙(?<!嘿嘿嘿)'
它将匹配不以嘿嘿嘿
结尾的字符串,如嘿嘿芙芙嘿嘿嘿
将不会被匹配到
正则表达式默认为贪婪匹配,即尽可能匹配最多的内容。若需要切换为懒惰匹配,可以在字符前加上?
,这样前面的匹配内容就会在该字符第一次被匹配到是停止扩展。如
芙芙嘿嘿嘿嘿嘿
在使用正则表达式
pythonr'(.*)嘿
将会匹配到整个字符串;而使用下面这个正则表达式
pythonr'(.*)?嘿
就只会匹配到芙芙
本文作者:GBwater
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!