标签:style blog http io ar color os 使用 sp
正则表达式工作中会经常用到,只是很少去系统的总结其中的一些基础的东西,导致有时候容易疏忽,上次修复一个url跳转的漏洞就考虑的简单,写错了正则,所以还是写篇文章来系统总结一下。正则表达式所有的编程语言几乎都是支持的,用于处理字符串匹配。大概流程就是根据正则表达式模式字符串,然后根据模式去匹配文本。记得我很久之前还写过一篇写正则匹配算法的文章正则表达式简易实现,有兴趣的可以看看。由于工作后用python较多,这篇文章就用python来写demo了。
首先看看正则表达式中的元字符,主要有.,^,$等等,也可以细分为基本元字符,数量元字符,位置元字符,特殊字符元字符以及分组等,详细解释如下,贪婪模式以及分组等在后面列出。
代码 | 说明 |
---|---|
基本元字符 | |
. | 匹配除了换行符“\n”以外的任意字符,在DOTALL模式中也能匹配换行符 |
\ | 转义字符,当我们的模式中有元字符如.,\时,需要进行转义 |
| | 逻辑或操作符 |
- | 字符集合定义一个区间,如后面的A-Z |
[...] | 字符集合,中括号中可以是字符集中的任意字符,可以逐个列出如[abc],也可以给出范围,如[a-c],如果里面的字符以^开头,表示取反。如[^abc]表示匹配不为a,b或者c的字符。如果字符集合中要包含特殊字符如],-,^等,则需要加转义字符\,或者也可以将],-放在开头,^不放在开头。要注意的是,也只有在字符集合中可以这样,其他地方还是要价转义字符。 |
位置元字符 | |
^ | 匹配字符串的开头(多行模式匹配每一行开头) |
$ | 匹配字符串的结尾(多行模式匹配每一行结尾) |
\A | 匹配字符串的开头 |
\Z | 匹配字符串的结尾 |
\b | 匹配单词边界,即单词开始或结束 |
\B | 不匹配单词边界,即[^b] |
数量元字符 | |
* | 匹配前一个字符(或者表达式)零次或多次 |
+ | 匹配前一个字符(或者表达式)一次或多次 |
? | 匹配前一个字符(或者表达式)零次或一次 |
{n} | 匹配前一个字符(或者表达式)n次 |
{m,n} | 匹配前一个字符(或者表达式)最少m次,最多n次 |
{m,} | 匹配前一个字符(或者表达式)最少m次 |
特殊元字符 | |
\d | 匹配数字,等同于[0-9] |
\D | 匹配非数字,等同于[^\d] |
\w | 匹配单词字符,即字母,数字,下划线,等同于[A-Za-z0-9_] |
\W | 匹配非单词字符,等同于[^\w] |
\s | 匹配空白字符,空白字符包括空格,\t, \r, \n等 |
\S | 匹配非空白字符,等同于[^\s] |
\n | 匹配回车符 |
\t | 匹配制表符 |
分组元字符 | |
(...) | 小括号中的内容表示一个分组,每匹配一个分组,编号加1.分组作为一个整体,后面可以加数量元字符。比如(abc){2} |
(?P<name>...) | 分组除了原有的编号外加个别名,如(?P<id>abc) 匹配abc并将组的别名设为id |
\number | 引用编号为number的分组匹配到字符串里,如(\d)abc\1 ,其中\1引用(\d)分组,该表达式可以匹配1abc1等 |
(?P=name) | 引用别名为name的分组匹配到字符串中,如(?P\<id>\d)abc(?P=id) ,该表达式可以匹配1abc1等 |
匹配模式可以配置为忽略大小写或者多行匹配,在python中常用的设置模式主要有如下几种:re.I(IGNORECASE): 忽略大小写。re.M(MULTILINE): 多行模式,该模式下^/$可以匹配每一行的开头/末尾。re.S(DOTALL): 点任意匹配模式,该模式.可以匹配任意字符。re.X(VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。以下两个正则表达式是等价的:
a = re.compile(r"""\d + # the integral part \. # the decimal point \d * # some fractional digits""", re.X) b = re.compile(r"\d+\.\d*")
此外,根据正则表达式不同还分贪婪模式和非贪婪模式。如果在表达式后面加?表示非贪婪模式,而如果不加在python中默认是贪婪模式。例如匹配的字符串为“abbbbc”,正则为“ab*”的时候,匹配结果是"abbbb";如果正则为"ab*?",则匹配结果为"a"。
大多数的编程语言里面也是用的反斜杠来转义,所以如果代码中要匹配"\"的话,就需要用四个反斜杠才能匹配。不过python里面用原生字符串解决了这个问题,只要在字符串前面加r就行,比如r"\d"
,不用原生字符串的话就要写成"\\d"
了。
先不管分组,看看比较基本的用法好了。光看文字说明也不一定清晰,还是栗子最实在。注意re.compile返回的Pattern对象,这样提前编译好在大批量匹配的时候会对性能有所提升。当然,也可以直接用re模块提供的各种方法来进行匹配。
#coding:utf8 import re def basic(): string_pat = [] string = "adbbb" pat = r"a.b*" #贪婪模式,匹配adbbb string_pat.append((string,pat)) string = "adb^?]bb" pat = r"a.b[][bc?^]*" ##不需要加转义字符,因为^没有放在最前面,而]放在了最前面 string_pat.append((string,pat)) string = "adbbb" pat = r"a.b*?" #非贪婪模式,匹配ad string_pat.append((string,pat)) string = "a\nbbb" pat = r"a.b*" #匹配不了\n, 匹配模式改为re.S才能匹配,即pattern=re.compile(pat, re.S) string_pat.append((string,pat)) string = "adbbb" pat = r"^a.b$" #匹配不了,不加$则可以成功匹配 string_pat.append((string,pat)) string = "kkk\nadb\nddd" pat = r"\Aadb" string_pat.append((string,pat)) string = "kkk\nadbcc\nddd" pat = r"^adb" #匹配失败 string_pat.append((string,pat)) for string,pat in string_pat: pattern = re.compile(pat) ret = pattern.match(string) if ret: print "string:%s, pat:%s, result:%s" % (repr(string), pat, ret.group()) else: print "string:%s, pat:%s, result:NO" % (repr(string), pat) ###运行结果 string:'adbbb', pat:a.b*, result:adbbb string:'adb^?]bb', pat:a.b[][bc?^]*, result:adb^?]bb string:'adbbb', pat:a.b*?, result:ad string:'a\nbbb', pat:a.b*, result:NO string:'adbbb', pat:^a.b$, result:NO string:'kkk\nadb\nddd', pat:\Aadb, result:NO string:'kkk\nadbcc\nddd', pat:^adb, result:NO</code>
Match对象是匹配的结果,包含的属性和方法如下:
属性
import re m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds") print "string:", m.string print "re:", m.re print "pos:", m.pos print "endpos:", m.endpos print "lastindex:", m.lastindex print "lastgroup:", m.lastgroup ##输出结果如下: string: Malcolm Reynolds re: <_sre.SRE_Pattern object at 0x10baa5200> pos: 0 endpos: 16 lastindex: 2 lastgroup: last_name
方法
##group函数示例 >>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds") >>> m.group() ##等价于m.group(0) 'Malcolm Reynolds' >>> m.group(1) 'Malcolm' >>> m.group(2) 'Reynolds' >>> m.group(1,2) ('Malcolm', 'Reynolds') >>> m.group('first_name') 'Malcolm' >>> m.group('last_name') 'Reynolds' >>> m.expand(r'\2 \1') 'Reynolds Malcolm' >>> m = re.match(r"(..)+", "a1b2c3") # Matches 3 times. >>> m.group(1) # Returns only the last match. 'c3' ##groups函数示例 >>> m = re.match(r"(\d+)\.?(\d+)?", "24") >>> m.groups() # Second group defaults to None. ('24', None) >>> m.groups('0') # Now, the second group defaults to '0'. ('24', '0') ##groupdict函数示例 >>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds") >>> m.groupdict() {'first_name': 'Malcolm', 'last_name': 'Reynolds'} ##start,end,span函数示例 >>>m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds") >>>m.start() 0 >>>m.start(2) 8 >>>m.end() 16 >>>m.end(1) 7 >>>m.span(1) (0, 7)
Pattern对象是编译好的正则表达式,通过re.compile构造,如re.compile(r"a.b")
,其属性有pattern,flags,groups和groupindex等,具体可以参见python docs。
此外,在多行模式re.M中,match只能匹配字符串开头,而search以及findall可以匹配每一行开头。见下面例子。
>>> re.match("c", "abcdef") # No match >>> re.search("c", "abcdef") # Match <_sre.SRE_Match object at ...> >>> re.match("c", "abcdef") # No match >>> re.search("^c", "abcdef") # No match >>> re.search("^a", "abcdef") # Match <_sre.SRE_Match object at ...> >>> re.match('X', 'A\nB\nX', re.MULTILINE) # No match >>> re.search('^X', 'A\nB\nX', re.MULTILINE) # Match <_sre.SRE_Match object at ...>
split用于将字符串按照指定的模式分割为列表,其中maxsplit指定最多分割次数。
>>> text = """Ross McFluff: 834.345.1254 155 Elm Street ... ... Ronald Heathmore: 892.345.3428 436 Finley Avenue ... Frank Burger: 925.541.7625 662 South Dogwood Way ... ... ... Heather Albrecht: 548.326.4584 919 Park Place""" >>> entries = re.split("\n+", text) ##按换行符分割 >>> entries ['Ross McFluff: 834.345.1254 155 Elm Street', 'Ronald Heathmore: 892.345.3428 436 Finley Avenue', 'Frank Burger: 925.541.7625 662 South Dogwood Way', 'Heather Albrecht: 548.326.4584 919 Park Place'] >>> [re.split(":? ", entry, 3) for entry in entries] [['Ross', 'McFluff', '834.345.1254', '155 Elm Street'], ['Ronald', 'Heathmore', '892.345.3428', '436 Finley Avenue'], ['Frank', 'Burger', '925.541.7625', '662 South Dogwood Way'], ['Heather', 'Albrecht', '548.326.4584', '919 Park Place']]
sub使用字符串repl或者一个函数返回值替换每个匹配的字符串。例子中对字符串中每个匹配的值的中间部分单词进行了shuffle操作,前后字母不变。count为最多替换次数,默认是全部替换。
>>> def repl(m): ... inner_word = list(m.group(2)) ... random.shuffle(inner_word) ... return m.group(1) + "".join(inner_word) + m.group(3) >>> text = "Professor Abdolmalek, please report your absences promptly." >>> re.sub(r"(\w)(\w+)(\w)", repl, text) 'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.' >>> re.sub(r"(\w)(\w+)(\w)", repl, text) 'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.'
返回全部匹配的子串,结果为列表形式。
>>> text = "He was carefully disguised but captured quickly by police." >>> re.findall(r"\w+ly", text) ['carefully', 'quickly']
如果需要匹配的子串的位置信息,那用finditer更好,因为它返回的是Match对象。
>>> text = "He was carefully disguised but captured quickly by police." >>> for m in re.finditer(r"\w+ly", text): ... print '%02d-%02d: %s' % (m.start(), m.end(), m.group(0)) 07-16: carefully 40-47: quickly
工作中常用的就是电话号码,url匹配等一些正则表达式,下面也一并总结下,如有错误请指出。
'^0\d{2,3}-?\d{7,8}$
r"^http(s)?://([\w-]+\.)*(com|cn|org|net)(:\d*)?(/.*)?$"
r"^http(s)?://([\w-]*\.)*((163\.com)|(baidu\.com)|(qq\.com))(:\d*)?(/.*)?$"
由于正则表达式也是第一次完整学习,参考了很多资料,主要有《python正则表达式指南》和python docs中关于re模块的章节。完整参考列表如下:
标签:style blog http io ar color os 使用 sp
原文地址:http://blog.csdn.net/sgbfblog/article/details/41412747