python正则匹配特殊字符_Python正则表达式笔记

做NLP离不开数据处理,做数据处理怎么可以不会正则表达式呢?本文涵盖了正则表达式的几个核心概念和常用函数,适合对正则表达式有基本了解的同学阅读,作为一个笔记。

1.概念

字符组(character class) 字符组的内容是在同一个位置能够匹配的若干字符,它的意思是“或”,用[···]表示。

多选分支(alternative) 假如Bob和Robert是两个表达式,但Bob | Robert能够同时匹配其中任意一个的正则表达式。在这样的组合中,子表达式称为“多选分支”。

反向引用(back reference) 表达式在匹配时,表达式引擎会将小括号()包含的表达式所匹配到的字符串记录下来,叫做捕获。在获取匹配结果的时候,小括号包含的表达式所匹配到的字符串可以单独获取。 \1 引用第1对括号内匹配到的字符串,\2引用第2对括号内匹配到的字符串,以此类推。 如果一对括号内包含另一对括号,则外层的括号先排序号。换句话说,哪一对的左括号(在前,那这一对为先。

匹配 一个正则表达式“匹配”一个字符串,其实是指这个正则表达式在字符串中找到匹配文本。严格地说,正则表达式a不能匹配cat,但是能匹配cat中的a。

子表达式(group) 子表达式是指整个表达式中的一部分,通常指括号内的表达式,或者是由|分隔的多选分支。量词的作用对象是它们之前紧邻的子表达式。如果量词之前紧邻的是一个括号包围的子表达式,整个子表达式都被视作一个单元。

非捕获型括号 (···)用来分组和捕获,而(?:···)表示只分组不捕获。例如

if ($input =~ m/^([-+]?[0-9]+(?:\.[0-9]*)?)([CF])$/)

即使[CF]两端的括号的确是排在第三位,它匹配的文本也会保存到$2中,因为(?:···)不会影响捕获计数。

环视(lookaround) 环视结构不匹配任何字符,只匹配文本中的特定位置,这一点与单词分界符\b、锚点^和$相似。肯定型顺序环视(positive lookahead)用(?=···)表示,例如(?=\d)表示如果当前位置右边的字符是数字则匹配成功。肯定型逆序环视(?<=\d)表示如果当前位置的左边有一位数字则匹配成功(也就是说,紧跟在数字后面的位置)。环视不占用字符。

2.特殊字符

3. Python re

3.1 pattern对象

正则表达式编译成pattern对象,pattern对象执行匹配和替代等操作。

>>> import re

>>> p = re.compile('ab*')

>>> p

re.compile('ab*')

re.compile()还接收flag参数,比如忽略大小写。

>>> p = re.compile('ab*', re.IGNORECASE)

\在Python和正则表达式中都是转义符,这就决定了如果想要在正则表达式中使用\作为普通字符,得先在Python中“转义”一次,再在正则表达式中“转义”一次。比如,想要匹配LaTeX中的\section,需要一个匹\section的正则表达式,也就是\\section。但直接把\\section放入re.compile()是不行的,因为Python遇到\还要“转义”一次,使Python代码中\\section这个字符串的正则表达式意义是\section,而不是想要的\\section意义。所以re.complie()真正需要的参数是\\\\section。要避免这种麻烦的写法,可以在正则表达式前加一个r。比如r"\\section"的正则表达式意义就是\section。

3.2 pattern对象的方法

regex.search() 扫描整个字符串找到第一个匹配的位置。如果有匹配返回match对象,如果没有返回None。可选参数pos和endpos表示期望匹配的字符范围,左闭右开。rx.search(s,0,50)从0开始扫描,扫描长度为50。

>>> pattern = re.compile("d")

>>> pattern.search("dog") # Match at index 0

<_sre.SRE_Match object; span=(0, 1), match='d'>

regex.match() 从头开始匹配零个或多个字符。如果匹配返回match对象,否则匹配None。可选参数pos和endpos作用同search。

regex.findall() 作用同re.findall(pattern,s,flags=0)。返回字符串中所有不重复(不重复指的是位置不重复)的匹配,以string列表的形式。字符串从左到右扫描,返回的匹配顺序如被发现的顺序。 此外,也有pos和endpos参数。

regex.findite() 作用同re.finditer(pattern, string, flags=0)。返回一个迭代器,产生所有不重复的match对象。此外,也有pos和endpos参数。

regex.sub(repl,string,count=0) 同re.sub(pattern, repl, string, count=0, flags=0)。返回将匹配到的substring替换成repl后的字符串。如果没有匹配,字符串不变。

repl可以包含反向引用,包含反向引用的时候需要以r'···'包围。

result = re.sub('abc', '', input) # Delete pattern abc

result = re.sub('abc', 'def', input) # Replace pattern abc -> def

result = re.sub(r'\s+', ' ', input) # Eliminate duplicate whitespaces

result = re.sub('abc(def)ghi', r'\1', input) # Replace a string with a part of itself

result = re.sub("(\d+) (\w+)", r"\2 \1") #exchange digits and word

如果repl中有转义符,转义符保持python中的转义功能。

>>>result = re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):',

... r'static PyObject*\npy_\1(void)\n{',

... 'def myfunc():')

>>>print (result)

static PyObject*

py_myfunc(void)

{

#两个\n都保留了换行意义。

match对象

match对象的布尔值总是真。

match = re.search(pattern, string)

if match:

process(match)

match.group([group1, ...]) group对应括号括起来的子表达式。这个方法返回一个或多个子表达式的匹配。参数是子表达式的序号。如果参数只有一个,就返回一个字符串,这时候也可以写成match[gid]。如果多个,就返回一个元组,元组元素对应每个序号的子表达式匹配的文本。如果没有参数,或参数中包含0,那么就返回整个匹配的文本。如果一个子表达式有多个匹配,返回最后一个匹配。

>>> 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')

>>> m = re.match(r"(..)+", "a1b2c3") # Matches 3 times.

>>> m.group(1) # Returns only the last match.

'c3'

回到regex.sub()。repl除了是字符串,还可以是函数。如果repl是函数,它的输入是match对象,返回一个最终想替换的字符串。

>>> def dashrepl(matchobj):

... if matchobj.group(0) == '-': return ' '

... else: return '-'

>>> re.sub('-{1,2}', dashrepl, 'pro----gram-files')

'pro--gram files'

#-{1,2}匹配一个或两个-,尽量匹配两个。如果匹配的是两个-,就替换成一个-。如果匹配的是一个-,就替换成空格。

match.start() 返回所匹配文本的起始位置。

match.end() 返回所匹配文本的结束位置。

match.span() 返回(start, end)的元组。

>>>import re

>>>p = re.compile('[a-z]+')

>>>m = p.match('tempo')

>>>>>> m.group()

'tempo'

>>> m.start(), m.end()

(0, 5)

>>> m.span()

(0, 5)

3.3 编译标志位

re.compile()可以传入flags参数,一定程度上改变正则表达式的工作方式。flag可以用全称也可以用简称。多个flags之间用|并列。

flags = 0

if multiline:

flags = re.M

if dotall:

flags |= re.S

if verbose:

flags |= re.X

if ignorecase:

flags |= re.I

if uni_code:

flags |= re.U

regex = re.compile(r'Test Pattern', flags)

3.4 贪婪和非贪婪

a*会匹配尽可能多的字符,这是贪婪的。

>>> s = '

Title'

>>> print(re.match('<.*>', s).group())

Title

想要非贪婪匹配,在量词后面加?匹配尽可能少的字符:*?,??,{m,n}?。

>>> print(re.match('<.*?>', s).group())

3.5 使用re.VERBOSE

re.VERBOSE标志位会增加正则表达式的可读性。使用re.VERBOSE时,不出现在字符组中的空格会被忽略,还可以使用#注释。

pat = re.compile(r"""\s* # Skip leading whitespace(?P[^:]+) # Header name\s* : # Whitespace, and a colon(?P.*?) # The header's value -- *? used to# lose the following trailing whitespace\s*$ # Trailing whitespace to end-of-line""", re.VERBOSE)

#对比

pat = re.compile(r"\s*(?P[^:]+)\s*:(?P.*?)\s*$")

参考资料

《精通正则表达式》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值