正则表达式
来源:互联网 发布:编程语言 使用范围 编辑:程序博客网 时间:2024/06/06 19:52
前言
在python里查找,替换字符串的方法是:index()、 find()、split()、 count()、 replace()等。但这些方法都只是最简单的字符串处理。比如:用index()方法查找单个子字符串,而且查找总是区分大小写的。为了使用不区分大小写的查找,可以使用s.lower()或者s.upper(),但要确认你查找的字符串的大小写是匹配的。replace() 和split() 方法有相同的限制。
如果使用string的方法就可以达到你的目的,那么你就使用它们。它们速度快又简单,并且很容易阅读。但是如果你发现自己要使用大量的if语句,以及很多字符串函数来处理一些特例,或者说你需要组合调用split() 和 join() 来切片、合并你的字符串,你就应该使用正则表达式。
阅读re模块的摘要信息可以了解到一些处理函数以及它们参数的一些概况。
>>> s = '100 NORTH BROAD ROAD'>>> s.replace('ROAD', 'RD.') '100 NORTH BRD. RD.'>>> s[:-4] + s[-4:].replace('ROAD', 'RD.') '100 NORTH BROAD RD.'>>> import re >>> re.sub('ROAD$', 'RD.', s) ⑤'100 NORTH BROAD RD.'
注意第一个参数ROAD $
,这是一个匹配‘ROAD’仅仅出现在字符串结尾的正则表达式。$
表示“字符串结尾”。(还有一个相应的表示“字符串开头”的字符 ^ )。正则表达式模块的re.sub()函数可以做字符串替换,它在字符串s中用正则表达式‘ROAD$’来搜索并替换成‘RD.’。它只会匹配字符串结尾的‘ROAD’,而不会匹配到‘BROAD’中的‘ROAD’,因为这种情况它在字符串的中间。
为了在正则表达式中表达这个独立的词,你可以使用‘\b’。它的意思是“在右边必须有一个分隔符”。
>>> re.sub('\\bROAD$', 'RD.', s) ①'100 BROAD'
为了解决‘\’字符传染的问题,可以使用原始字符串。这只需要在字符串的前面添加一个字符‘r’。它告诉python,字符串中没有任何字符需要转义。‘\t’是一个制表符,但r‘\t’只是一个字符‘\’紧跟着一个字符t。我建议在处理正则表达式的时候总是使用原始字符串。否则,会因为理解正则表达式而消耗大量时间(本身正则表达式就已经够让人困惑的了)。
>>> re.sub(r'\bROAD$', 'RD.', s) ②'100 BROAD'
案例研究: 罗马数字
你肯定见过罗马数字,即使你不认识他们。你可能在版权信息、老电影、电视、大学或者图书馆的题词墙看到(用Copyright MCMXLVI” 表示版权信息,而不是用 “Copyright 1946”),你也可能在大纲或者目录参考中看到他们。这种系统的数字表达方式可以追溯到罗马帝国(因此而得名)。
在罗马数字中,有七个不同的数字可以以不同的方式结合起来表示其他数字。
I = 1 V = 5 X = 10 L = 50 C = 100 D = 500 M = 1000
下面是几个通常的规则来构成罗马数字:
- 大部分时候用字符相叠加来表示数字。I是1, II是2, III是3。VI是6(挨个看来,是“5 和 1”的组合),VII是7,VIII是8。
- 含有10的字符(I,X,C和M)最多可以重复出现三个。为了表示4,必须用同一位数的下一个更大的数字5来减去一。不能用IIII来表示4,而应该是IV(意思是比5小1)。40写做XL(比50小10),41写做XLI,42写做XLII,43写做XLIII,44写做XLIV(比50小10并且比5小1)。
- 有些时候表示方法恰恰相反。为了表示一个中间的数字,需要从一个最终的值来减。比如:9需要从10来减:8是VIII,但9确是IX(比10小1),并不是VIII(I字符不能重复4次)。90是XC,900是CM。
- 表示5的字符不能在一个数字中重复出现。10只能用X表示,不能用VV表示。100只能用C表示,而不是LL。
- 罗马数字是从左到右来计算,因此字符的顺序非常重要。DC表示600,而CD完全是另一个数字400(比500小100)。CI是101,IC不是一个罗马数字(因为你不能从100减1,你只能写成XCIX,表示比100小10,且比10小1)。
re模块最基本的方法是search()函数。
>>> import re>>> pattern = '^M?M?M?$' >>> re.search(pattern, 'M') <_sre.SRE_Match object at 0106FB58>>>> re.search(pattern, 'MM') >>> re.search(pattern, '') ⑥<_sre.SRE_Match object at 0106F4A8>
有趣的是,空字符串也能匹配成功,因为正则表达式中的所有M都是可选的。
? 表示匹配是可选的
使用语法{n,m},{1,4} 匹配1到4个前面的模式
>>> pattern = '^M{0,3}$'
这个正则表达式的意思是“匹配字符串开始,然后是任意的0到3个M字符,再是字符串结尾”。0和3的位置可以写任意的数字。如果你想表示可以匹配的最小次数为1次,最多为3次M字符,可以写成M{1,3}。
(A|B) 匹配A模式或者B模式中的一个
记住:(A|B|C)的意思是“只匹配A,B或者C中的一个”。你匹配了XL,因此XC和L?X?X?X?被忽略,紧接着将检查字符串结尾。MCMXL在罗马数字中表示1940。
>>> pattern = '^M?M?M?(CM|CD|D?C?C?C?)(XC|XL|L?X?X?X?)$'>>> pattern = '^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'
松散正则表达式
到目前为止,你只是处理了一些小型的正则表达式。就像你所看到的,他们难以阅读,甚至你不能保证半年后,你还能理解这些东西,并指出他们是干什么的。所以你需要在正则表达式内部添加一些说明信息。
>>> re.search(pattern, 'M', re.VERBOSE) ①<_sre.SRE_Match object at 0x008EEB48>
注意,如果要使用松散正则表达式,需要传递一个叫re.VERBOSE的参数。就像你看到的那样,正则表达式中有很多空白符,他们都被忽略掉了。还有一些注释信息,当然也被正则表达式忽略掉。当空白符和注释信息被忽略掉后,这个正则表达式和上面的是完全一样的,但是它有更高的可读性。
案例研究: 解析电话号码
\d 匹配所有0-9的数字. \D 匹配除了数字外的所有字符
>>> phonePattern = re.compile(r'^(\d{3})-(\d{3})-(\d{4})$') ①>>> phonePattern.search('800-555-1212').groups() ②('800', '555', '1212')>>> phonePattern.search('800-555-1212-1234') ③>>> phonePattern.search('800-555-1212-1234').groups() ④Traceback (most recent call last): File "<stdin>", line 1, in <module>AttributeError: 'NoneType' object has no attribute 'groups'
- 我们通常从左到右的阅读正则表达式。首先是匹配字符串开始位置,然后是(\d{3})。\d{3}表示什么意思?\d表示任意的数字(0到9),{3}表示一定要匹配3个数字。这个是你前面看到的{n,m}表示方法。把他们放在圆括号中,表示必须匹配3个数字,并且把他们记做一个组。分组的概念我们后面会说到。
- 为了使用正则表达式匹配到的这些分组,需要对search()函数的返回值调用groups()方法。它会返回一个这个正则表达式中定义的所有分组结果组成的元组。
>>> phonePattern = re.compile(r'^(\d{3})-(\d{3})-(\d{4})-(\d+)$')
(\d+)这个分组里的内容是匹配一个或更多个数字
>>> phonePattern = re.compile(r'^(\d{3})\D+(\d{3})\D+(\d{4})\D+(\d+)$')
\D+,这是什么?好吧,\D匹配除了数字以外的任意字符,+的意思是一个或多个。因此\D+匹配一个或一个以上的非数字字符。这就是你用来替换连字符的东西,它用来匹配不同的分隔符。
用\D+替换-,意味着你可以匹配分隔符为空格的情况。
用正则表达式处理电话号码没有分隔符的情况。 >>> phonePattern = re.compile(r'^(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')
把所有的+换成了。号码之间的分隔符不再用\D+来匹配,而是使用\D。还记得+表示一个或更多吧?好,现在可以解析号码之间没有分隔符的情况了。
现在在字符串的开头可能有一些你想忽略掉的不确定的字符。为了匹配到想要的数据,你需要跳过他们。我们来看看不明确匹配字符串开始的方法。 >>> phonePattern = re.compile(r'(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')
这是最终的答案!注意正则表达式没有^。不会再匹配字符串开始位置了。正则表达式不会匹配整个字符串,而是试图找到一个字符串开始匹配的位置,然后从这个位置开始匹配。
第二种松散正则表达式:
>>> phonePattern = re.compile(r''' # don't match beginning of string, number can start anywhere (\d{3}) # area code is 3 digits (e.g. '800') \D* # optional separator is any number of non-digits (\d{3}) # trunk is 3 digits (e.g. '555') \D* # optional separator (\d{4}) # rest of number is 4 digits (e.g. '1212') \D* # optional separator (\d*) # extension is optional and can be any number of digits $ # end of string ''', re.VERBOSE)>>> phonePattern.search('work 1-(800) 555.1212 #1234').groups() ①('800', '555', '1212', '1234')
小结
这只是正则表达式能完成的工作中的冰山一角。换句话说,尽管你可能很受打击,相信我,你已经不是什么都不知道了。
现在,你应该已经熟悉了下面的技巧:
^ 匹配字符串开始位置。$ 匹配字符串结束位置。\b 匹配一个单词边界。\d 匹配一个数字。\D 匹配一个任意的非数字字符。x? 匹配可选的x字符。换句话说,就是0个或者1个x字符。x* 匹配0个或更多的x。x+ 匹配1个或者更多x。x{n,m} 匹配n到m个x,至少n个,不能超过m个。(a|b|c) 匹配单独的任意一个a或者b或者c。(x) 这是一个组,它会记忆它匹配到的字符串。你可以用re.search返回的匹配对象的groups()函数来获取到匹配的值。
正则表达式非常强大,但它也并不是解决每一个问题的正确答案。你需要更多的了解来判断哪些情况适合使用正则表达式。某些时候它可以解决你的问题,某些时候它可能带来更多的问题。
参考:
http://old.sebug.net/paper/books/dive-into-python3/regular-expressions.html
- 【正则表达式】正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- 正则表达式
- [C#]面向对象
- Effiective Java读书笔记——第2章 创建和销毁对象
- 历届试题 国王的烦恼 (并查集)
- leetcode 520 python
- java安全架构____RSA加密解密
- 正则表达式
- Android AccessibilityService拦截事件及VR眼镜返回按键捕捉
- PAT 1004
- Codeforces Round #401(Div. 2)E. Hanoi Factory【贪心+栈】
- Mathtype与word字号对照
- 通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的
- OSI七层协议模型和TCP/IP四层模型比较
- [Haskell on HackerEarth] Modify Sequence
- SD卡的使用