Skip to content

Instantly share code, notes, and snippets.

@songxiaofeng1981
Created November 21, 2012 06:51
Show Gist options
  • Save songxiaofeng1981/4123464 to your computer and use it in GitHub Desktop.
Save songxiaofeng1981/4123464 to your computer and use it in GitHub Desktop.
python:regrex-explain
#!/usr/bin/env python
#encoding=utf-8
#vim ft=python
#abstract: 演示Python中正则表达式的基本用法
#tag:Python RegExp example
import re
#辅助函数,打印一个matchobject的信息
def print_match(s, m):
if m:
#group()函数返回指定group的内容,默认参数0返回整个匹配,非0的参数表示子匹配
print s, '\tmatchobject:', m.group()
else:
print s, '\tnot match'
def test1():
s = 'hello world'
r1 = 'h.*l'
r2 = '.*l'
#re.match和re.search的区别
#re.match必须从字符串第一个字符开始匹配,re.search可以从中间任意字符开始
print_match('re.match', re.match(r1, s))
print_match('re.search', re.search(r2, s))
def test2():
s = 'abbabbabbabba'
#默认是贪婪匹配,在*,?,{}后面加上一个问号,变成非贪婪匹配
print_match('greed\t', re.search('a(.*)a', s))
print_match('non-greed', re.search('a(.*?)a', s))
def test3():
s = 'abbabbabbabba'
#{a,b}最少匹配a次,最多匹配b次,默认贪婪,加上问号则变成非贪婪匹配
print_match('count\t', re.search('a(bba){1,2}', s))
print_match('non-greed', re.search('a(bba){1,2}?', s))
def test4():
s = 'abbabbabbabba'
#中括号表示可以使用其中的任意一个字符
print_match('[]\t', re.search('a[ab]*', s))
#单竖线表示两边的都可以匹配,两边可以不只是一个字节
print_match('|\t', re.search('(a|b)*', s))
def test5():
s = 'abbabbabbabba'
#圆括号表示提取这段内容作为一个小组,以后可以用\1, \2等来引用
m = re.search('a(b)(b.b)(.*)ab', s)
print_match('()\t', m)
#group(n)是第n个圆括号中的内容,groups()列出所有小组,但是不包括group(0)
print '\tm.group(1):', m.group(1)
print '\tm.groups:', m.groups()
s = 'abcdremove_meefg'
m = re.search('remove_me', s)
#matchobject类有start和end两个方法,可以返回某个组的起始位置和结束位置
#这里演示的是靠这两个位置和切片函数,实现删除字符串的部分内容
s = s[:m.start()] + s[m.end():]
print 'start()/end()\t', s
s = 'abcdremove_meefg'
#start和end后面可以加上参数,用于指定某个小组
#默认不写的话就是0,表示整个匹配
m = re.search('d(remove_me)e', s)
s = s[:m.start(1)] + s[m.end(1):]
print 'start(1)/end(1)\t', s
def test6():
#(?)是一组特殊的用法,分别举例如下
s = 'aA'
#问号后面加上几个特殊的字母,可以对整个匹配进行一些设置
#在这里设置的优点是,只要告诉别人这个pattern,就知道设置了
#后面可以跟的字符有‘iLmsux’,可以任意组合,注意大小写,常用的几个介绍如下
#i,忽略大小写
#m,^可以在多行文本中匹配每行开头,默认情况下只能匹配字符串开头,同理,$可以匹配每行末尾
#s,'.'可以匹配换行符,默认是只能匹配除了换行符以外的任意字符
print_match('(?i)\t', re.search('(?i)aa', s))
s = 'a\nAa'
print_match('(?mi)\t', re.search('(?mi)aa', s))
print_match('(?mis)\t', re.search('(?mis)....', s))
#(?:)和一对圆括号差不多,唯一不同是里面的内容不会记录到group里面
s = 'abcdef'
m = re.search('(?:a)(..)', s)
print_match('(?:)\t', m)
print '\tgroups:', m.groups()
print '\tgroupdict:', m.groupdict()
#(?P<id>)可以给一个group起名字,后面可以用名字引用这个group,增加可读性
m = re.search('(?P<test>a)(?P<test2>..)', s)
print_match('(?P)\t', m)
print '\tgroups:', m.groups()
print '\tgroupdict:', m.groupdict()
print '\tgroup(test):', m.group('test')
print '\tgroup(test2):', m.group('test2')
#(?#)就是注释,会被忽略
#(?P=id)可以引用前面定义好的组名
s = 'abcdefabcdef'
m = re.search('(?#commnt)(?P<a>...).*(?P=a)', s)
print_match('(?P=name)', m)
print '\tgroups:', m.groups()
print '\tgroupdict:', m.groupdict()
#下面四个都是零长度匹配,前两个是前向匹配,后两个是后向匹配
#(?=)要求后面必须要有指定的内容,前面的内容才有效
#(?!)要求后面必须没有指定的内容,前面的内容才有效
#(?<=)要求前面必须是指定内容,后面的内容才有效
#(?<!)要求前面必须不是指定内容,后面的内容才有效
#反正比较难以用语言解释,看看下面四个语句运行的结果,应该能帮助理解
s = 'abcdeabbbb'
print_match('(?=)\t', re.search('ab(?=cde)...', s))
print_match('(?!)\t', re.search('ab(?!cde)...', s))
#注意,下面两个要求括号中的匹配长度是固定的,比如不能有*或{2, 3}这种匹配不定长的语法
print_match('(?<=)\t', re.search('.(?<=a)b', s))
print_match('(?<!)\t', re.search('.(?<!a)b', s))
#这是一个很特别的语法,和C的?:差不多,判断前面一个组是否匹配上了,如果匹配上,那么这段内容等效于y,否则等效于n(y和n可以是任意pattern),一般用法就是用来匹配括号,比如下面的语句,同样的语句,可以匹配'abc'或'[abc]',但是不能匹配'[abc'
s = 'abc'
print_match('(?(id)y|n)', re.search('(\[)?abc(?(1)\])', s))
s = '[abc]'
print_match('(?(id)y|n)', re.search('(\[)?abc(?(1)\])', s))
def test7():
s = 'abcdef'
#re.compile返回一个RegexObject,如果某个pattern要经常使用,这么弄可以提高效率,但是如果只是少量的使用,不会有明显区别
e = re.compile('abcde')
print_match('re.compile', e.search(s))
def test8():
#下面是re.sub()函数的用法,这个函数用于实现替换,还有一个subn,功能和sub一样,但是返回值是一个tuple,多了一个返回值,表示总共替换了几次
#如果要实现把a换成b,如下
print 're.sub()', re.sub('a', 'b', 'ababa')
#可以限制只替换n次
print 're.sub()', re.sub('a', 'b', 'aaaaa', 3)
#第二个参数可以很丰富多彩,即可以是一个字符串,也可以是一个函数名,也可以是一个lamda表达式
#另外,还可以通过\1来引用第一个子匹配,\g<id>引用(?P<id>)定义的子匹配,\g<1>和\1等效,但是\g<2>0可以表示第二个子匹配后面加上0,这个用\20就无法表示了
print 're.sub()', re.sub('(?P<aaa>a)bcd(e)', r'\2\g<aaa>\1', 'abcde')
#如果第二个参数是函数名或者lamda表达式,表示用这个函数的返回值,来替换匹配的内容
print 're.sub()', re.sub('a', lambda m: 'b', 'abababa')
#要注意的就是,这个函数是有一个参数的,类型是matchobject,就是整个匹配的信息
print 're.sub()', re.sub('a', lambda m: '(' + str(m.start()) + ',' + str(m.end()) + ')', 'babbbbab')
def test9():
s = 'abcdefgabcdefgabcdefg'
#re.findall用于查找所有的匹配(re.search只匹配一次,后面的就忽略了)
#返回值是一个list,元素可能是字符串,也可能是tuple,取决于有多少个子匹配
#如果没有子匹配,list中是每次匹配的整个内容
print 're.findall', re.findall('abc', s)
#如果有一个子匹配,list中是这个子匹配的内容
#如果有多个子匹配,list中是这些子匹配构成的tuple
print 're.findall', re.findall('(a)b(c)', s)
#如果需要得到多次匹配的MatchObject,则需要用re.finditer,他的返回值就是MatchObject的List
for m in re.finditer('abc', s):
print_match('re.finditer()', m)
#另外还有一些常用的,比如
#\A 匹配字符串开头 \Z匹配字符串结尾
#\d 匹配数字 \D 匹配所有非数字字符
#\s 匹配空白字符 \S 匹配非空白字符
#\w 匹配数字字母下划线 \W 反之
if __name__ == '__main__':
test1()
test2()
test3()
test4()
test5()
test6()
test7()
test8()
test9()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment