常见的特殊字符:
- . ——(点)在默认模式,匹配除了换行的人一字符
- ^——(插入符合)。匹配字符串的开头,并且在MUlTILINE模式也匹配换行后的首个符号
- $——匹配字符串尾或者在字符串尾的换行符的前一个字符,在MUTLINE模式下也会匹配换行符之前的文本。
- *—— 对他前面的正则式匹配0到任意次重复,尽量多的匹配字符串。
- +—— 对他前面的正则式匹配1到任意次重复
- ?——对他前面的正则式匹配0到1次重复。ab?会匹配'a'或者'ab'
- *?,+?,??——‘*’、‘+’、‘?’修饰符都是贪婪的;他们在字符串进行尽可能多的匹配。如果正则式
<.*>
希望找到'<a> b <c>'
,它将会匹配整个字符串,而不仅是'<a>'
。在修饰符之后添加?
将使样式以 非贪婪`方式或者 :dfn:`最小 方式进行匹配; 尽量 少 的字符将会被匹配。 使用正则式<.*?>
将会仅仅匹配'<a>'
。 - {m}——对其之前的正则式指定匹配m个重复;少于m的话就会导致匹配失败
- {m,n}——对正则式进行m到n次匹配,在m和n之间取尽量多。
- {m,n}?——前一个修饰符的非贪婪模式,只匹配尽量少的字符次数。比如,对于
'aaaaaa'
,a{3,5}
匹配 5个'a'
,而a{3,5}?
只匹配3个'a'
。 - \—— 转义特舒服(允许你匹配'*'、'?')
- []——用于表示一个字符集合。在一个集合中:
- 字符可以单独列出,比如[amk]匹配
'a'
,'m'
, 或者'k'
。 - 可以表示字符范围,通过用'-'将两个字符连起来。比如
[a-z]
将匹配任何小写ASCII字符,[0-5][0-9]
将匹配从00
到59
的两位数字,[0-9A-Fa-f]
将匹配任何十六进制数位。 如果-
进行了转义 (比如[a\-z]
)或者它的位置在首位或者末尾(如[-a]
或[a-]
),它就只表示普通字符'-'
。 - 特殊字符在集合中,失去它的特殊含义。比如
[(+*)]
只会匹配这几个文法字符'('
,'+'
,'*'
, or')'
。 - 字符类如
\w
或者\S
(如下定义) 在集合内可以接受,它们可以匹配的字符由 ASCII 或者 LOCALE 模式决定。 - 不在集合范围内的字符可以通过取反来进行匹配。比如
[^5]
将匹配所有字符,除了'5'
,[^^]
将匹配所有字符,除了'^'
.^
如果不在集合首位,就没有特殊含义。 - 在集合内要匹配一个字符
']'
,有两种方法,要么就在它之前加上反斜杠,要么就把它放到集合首位。比如,[()[\]{}]
和[]()[{}]
都可以匹配括号。
- 字符可以单独列出,比如[amk]匹配
- |—— A|B,A和B可以式任意正则表达式,创建一个正则表达式,匹配A或者B。任意个正则表达式可以用'|'连接。
- (……)——(组合),匹配括号内的任意正则表达式,并标识出组合的开始和结尾。匹配完成后,组合的内容可以被获取,并可以在之后用\number转义序列进行再次匹配。
扩展标记法
(一个
'?'
跟随'('
并无含义)(?……)。
'?'
后面的第一个字符决定了这个构建采用什么样的语法。这种扩展通常并不创建新的组合;(?P<name>...)
是唯一的例外。 以下是目前支持的扩展。
-
(?:…)
正则括号的非捕获版本。 匹配在括号内的任何正则表达式,但该分组所匹配的子字符串 不能 在执行匹配后被获取或是之后在模式中被引用。
>>> a = '123dd' >>> re.match('(?:\d+)', a).groups() () >>> re.match('(\d+)', a).groups() ('123',) >>>
-
(?P<name>…)
(命名组合)类似正则组合,但是匹配到的子串组在外部是通过定义的 name 来获取的。组合名必须是有效的Python标识符,并且每个组合名只能用一个正则表达式定义,只能定义一次。一个符号组合同样是一个数字组合,就像这个组合没有被命名一样。
-
(?P=name)
反向引用一个命名组合;它匹配前面那个叫 name 的命名组中匹配到的串同样的字串。
import re s=re.search(r'\((?P<areacode>\d{3})\) (?P<prefix>\d{3})-(?P<number>\d{4})', '(800) 555-1212').groupdict() print(s) #输出结果 # {'areacode': '800', 'prefix': '555', 'number': '1212'}
-
(?#…)
注释;里面的内容会被忽略。
>>> re.match('(?P<data>\d+).*?(?#this is coment)', a).groupdict() {'data': '123'} >>>
-
(?=…)
匹配
…
的内容,但是并不消费样式的内容。这个叫做 lookahead assertion。比如,Isaac (?=Asimov)
匹配'Isaac '
只有在后面是'Asimov'
的时候。import re s=re.findall(r'\w+(?= van Rossum)', ''' Guido van Rossum Tim Peters Alex Martelli Just van Rossum ''') print(s) # 输出结果 # ['Guido', 'Just']
>>> re.match('\w+(?=\d)', 'aa123').group() 'aa12' # 为什么这个正则是4位呢?
-
(?!…)
匹配
…
不符合的情况。这个叫 negative lookahead assertion (前视取反)。比如说,Isaac (?!Asimov)
只有后面 不 是'Asimov'
的时候才匹配'Isaac '
。以下可作为匹配二级域名的正则re.findall(r'http://(?:\w+\.)*(\w+\.com)', 'http://google.com http://www.google.com http://code.google.com') ['google.com', 'google.com', 'google.com']
-
(?<=…)
匹配字符串的当前位置,它的前面匹配
…
的内容到当前位置。这叫:dfn:positive lookbehind assertion (正向后视断定)。(?<=abc)def
会在'abcdef'
中找到一个匹配,因为后视会往后看3个字符并检查是否包含匹配的样式。包含的匹配样式必须是定长的,意思就是abc
或a|b
是允许的,但是a*
和a{3,4}
不可以。注意以 positive lookbehind assertions 开始的样式,如(?<=abc)def
,并不是从 a 开始搜索,而是从 d 往回看的。你可能更加愿意使用 search() 函数,而不是 match() 函数: -
(?<!…)
匹配当前位置之前不是
...
的样式。 -
(?(id/name)yes-pattern|no-pattern)
如果给定的 id 或 name 存在,将会尝试匹配
yes-pattern
,否则就尝试匹配no-pattern
,no-pattern
可选,也可以被忽略。比如,(<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$)
是一个email样式匹配,将匹配'<user@host.com>'
或'user@host.com'
,但不会匹配'<user@host.com'
,也不会匹配'user@host.com>'
。
常见的特殊序列:
-
\number
匹配数字代表的组合。每个括号是一个组合,组合从1开始编号。比如
(.+) \1
匹配'the the'
或者'55 55'
, 但不会匹配'thethe'
(注意组合后面的空格)。这个特殊序列只能用于匹配前面99个组合。如果 number 的第一个数位是0, 或者 number 是三个八进制数,它将不会被看作是一个组合,而是八进制的数字值。在'['
和']'
字符集合内,任何数字转义都被看作是字符。具体其他数字,待以后找到再更新 -
\A
只匹配字符串开始。
-
\b
匹配空字符串,但只在单词开始或结尾的位置。一个单词被定义为一个单词字符的序列。注意,通常
\b
定义为\w
和\W
字符之间,或者\w
和字符串开始/结尾的边界, 意思就是r'\bfoo\b'
匹配'foo'
,'foo.'
,'(foo)'
,'bar foo baz'
但不匹配'foobar'
或者'foo3'
。 -
\B
匹配空字符串,但 不 能在词的开头或者结尾。意思就是
r'py\B'
匹配'python'
,'py3'
,'py2'
, 但不匹配'py'
,'py.'
, 或者'py!'
.\B
是\b
的取非, -
\d
匹配任何Unicode十进制数
-
\D
匹配任何非十进制数字的字符
-
\s
匹配任何Unicode空白字符
-
\S
匹配任何非空白字符。
-
\w
匹配Unicode词语的字符
-
\W
匹配非单词字符的字符。
-
\Z
只匹配字符串尾。
模块内容
- re.compile(pattern,flags=0)。将正则表达式的样式编译为一个正则表达式 对象,可以用于匹配,通过这个对象的方法match(),search()以及其他。
import re #序列 prog = re.compile(pattern) result = prog.match(string) #等价于 result = re.match(pattern, string)
- re.search(pattern,string,flags=0)。扫描整个字符串找到匹配样式的第一个位置,并返回一个相应的匹配对象。如果没有,就返回None
# 可选的第二个参数 pos 给出了字符串中开始搜索的位置索引;默认为 0,它不完全等价于字符串切片; '^' 样式字符匹配字符串真正的开头,和换行符后面的第一个字符,但不会匹配索引规定开始的位置。 # 可选参数 endpos 限定了字符串搜索的结束;它假定字符串长度到 endpos , 所以只有从 pos 到 endpos - 1 的字符会被匹配。如果 endpos 小于 pos,就不会有匹配产生;另外,如果 rx 是一个编译后的正则对象, rx.search(string, 0, 50) 等价于 rx.search(string[:50], 0)。 >>> pattern = re.compile("d") >>> pattern.search("dog") # Match at index 0 <re.Match object; span=(0, 1), match='d'> >>> pattern.search("dog", 1) # No match; search doesn't include the "d"
- re.match(pattern,string,flags=0)。如果string开始的0或者多个字符匹配到了正则表达式样式,就返回一个相应的匹配对象。如果没有匹配,就返回None,re.match()只匹配字符串的开始位置,而不匹配每行开始。
>>> pattern = re.compile("o") >>> pattern.match("dog") # No match as "o" is not at the start of "dog". >>> pattern.match("dog", 1) # Match as "o" is the 2nd character of "dog". <re.Match object; span=(1, 2), match='o'>
- re.fullmatch(pattern,string,flags=0)。如果整个string匹配到正则表达式样式,就返回一个相应的匹配对象。否则就返回一个None
- re.split(patternn,string,maxsplit=0,flags=0)。用pattern分开string。如果再pattern中捕获到括号,那么所有的组里的文字也会包含在列表里。如果maxsplit非零,最多进行maxsplit次分隔,剩下的字符全部返回到列表的最后一个元素。
>>> re.split(r'\W+', 'Words, words, words.') ['Words', 'words', 'words', ''] >>> re.split(r'(\W+)', 'Words, words, words.') ['Words', ', ', 'words', ', ', 'words', '.', ''] >>> re.split(r'\W+', 'Words, words, words.', 1) ['Words', 'words, words.'] >>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE) ['0', '3', '9'] # 如果分隔符里有捕获组合,并且匹配到字符串的开始,那么结果将会以一个空字符串开始。对于结尾也是一样 >>> re.split(r'(\W+)', '...words, words...') ['', '...', 'words', ', ', 'words', '...', ''] # 这样的话,分隔组将会出现在结果列表中同样的位置。 # 样式的空匹配仅在与前一个空匹配不相邻时才会拆分字符串。 >>>re.split(r'\b', 'Words, words, words.') ['', 'Words', ', ', 'words', ', ', 'words', '.'] re.split(r'\W*', '...words...') ['', '', 'w', 'o', 'r', 'd', 's', '', ''] re.split(r'(\W*)', '...words...') ['', '...', '', '', 'w', '', 'o', '', 'r', '', 'd', '', 's', '...', '', '', '']
- re.findall(pattern,string,flags=0)。对string返回一个不重复的pattern的匹配列表,string从左到右进行扫描,匹配按找到的顺序返回。如果样式里存在一到多个组,就返回一个组合列表;就是一个元组的列表。
- re.finditer(pattern,strig,flags=0)。pattern在string里所有的非重复匹配,返回一个迭代器iterator保存了匹配对象。string从左到右扫描,匹配按顺序排列。
- re.sub(pattern,repl,strig,count=0,flags=0)。返回通过使用repl替换在string最左边非重叠出现的pattern而获得的字符串。正则表达式中的\2和\1和\3不仅分组还占位
# pattern可以是一个字符串也可以是一个正则,用于匹配要替换的字符,如果不写,字符串不做修改。 # repl是将会被替换的值,repl可以是字符串也可以是一个方法.repl如果是一个function,每一个被匹配到的字段串执行替换函数。 # \g<1> 代表前面pattern里面第一个分组,可以简写为\1,\g<0>代表前面pattern匹配到的所有字符串。 # count是pattern被替换的最大次数,默认是0会替换所有。有时候可能只想替换一部分,可以用到count 实例1: a = re.sub(r'hello', 'i love the', 'hello world') print(a)<br data-filtered="filtered">'i love the world' #hello world里面的hello被 i love the替换 实例2: >>> a = re.sub(r'(\d+)', 'hello', 'my numer is 400 and door num is 200') >>> a 'my numer is hello and door num is hello' #数字400 和 200 被hello替换 实例3: a = re.sub(r'hello (\w+), nihao \1', r'emma','hello sherry, nihao sherry') >>> a 'emma' #\1代表第一个分组的值即sherry,因为有两个sherry,所以用\1可以指代第二个,这样整个字符串被emma替换 代码解析: 其中()里边的是要匹配出来的结果,\1代表就是匹配出来的结果sherry,后边写\1的目的其实就是为了让正则不重复,所以前边正则匹配出来的结果就是 sherry,然后进行替换最后结果,就变成了emma 实例4:替换年月日的顺序的正则 >>> a = re.sub('(\d{4})-(\d{2})-(\d{2})', r'\2-\3-\1', '2018-06-07') >>> a '06-07-2018' >>> a = re.sub('(\d{4})-(\d{2})-(\d{2})', r'\g<2>-\g<3>-\g<1>', '2018-06-07') >>> a '06-07-2018' #\2 和 \g<2> 指代的的都是前面的第二个分组 实例5: 替换中的数字为大写 import re def replace_num(str): numDict = {'0':'〇','1':'一','2':'二','3':'三','4':'四','5':'五','6':'六','7':'七','8':'八','9':'九'} print(str.group()) return numDict[str.group()] my_str = '2018年6月7号' a = re.sub(r'(\d)', replace_num, my_str) print(a) #每次匹配一个数字,执行函数,获取替换后的值
- re.subn(pattern,repl,string,count=0,flags=0)。行为与sub()相同,但是返回一个元组(字符串,替换次数)
- re.escape(pattern)。转义pattern中的特殊字符。如果你想对任意可能包含正则表达式元字符的文本字符串进行匹配,他就是有用的。
匹配对象
match = re.search(pattern, string)
if match:
process(match)
- Match.expand(template)。对 template 进行反斜杠转义替换并且返回,就像 sub() 方法中一样。
-
Match.
group
([group1, ...])返回一个或者多个匹配的子组。如果只有一个参数,结果就是一个字符串,如果有多个参数,结果就是一个元组(每个参数对应一个项),如果没有参数,组1默认到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')
-
Match.
groups
(default=None)。返回一个元组,包含所有匹配的子组,在样式中出现的从1到任意多的组合。>>> m = re.match(r"(\d+)\.(\d+)", "24.1632") >>> m.groups() ('24', '1632') # 如果我们使小数点可选,那么不是所有的组都会参与到匹配当中。这些组合默认会返回一个 None ,除非指定了 default 参数。 >>> >>> 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')
-
Match.
groupdict
(default=None)返回一个字典,包含了所有的 命名 子组。key就是组名。 default 参数用于不参与匹配的组合;默认为
None
。 例如>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds") >>> m.groupdict() {'first_name': 'Malcolm', 'last_name': 'Reynolds'}
贪婪匹配
最后需要特别指出的是,正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。举例如下,匹配出数字后面的0
:
>>> re.match(r'^(\d+)(0*)$', '102300').groups()
('102300', '')
由于\d+
采用贪婪匹配,直接把后面的0
全部匹配了,结果0*
只能匹配空字符串了。
必须让\d+
采用非贪婪匹配(也就是尽可能少匹配),才能把后面的0
匹配出来,加个?
就可以让\d+
采用非贪婪匹配:
>>> re.match(r'^(\d+?)(0*)$', '102300').groups()
('1023', '00')
例题:
1、判断一个字符串是否是合法的Email?
2、模拟scanf
3、制作一个电话本
>>> 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, 4) 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']]
4、匹配以“www”起始且以“.com”结尾的简单Web域名:例如,http://www.yahoo.com ,也支持其他域名,如.edu .net等
m = re.search(r'w{3}\.[a-zA-Z]+\.(com|edu|net)',s)
5、有一个文件,文件名为output_1981.10.21.txt 。下面使用Python: 读取文件名中的日期时间信息,并找出这一天是周几。将文件改名为output_YYYY-MM-DD-W.txt (YYYY:四位的年,MM:两位的月份,DD:两位的日,W:一位的周几,并假设周一为一周第一天)
import re
import time
import datetime
filename = "output_1981.10.21.txt"
m = re.search("output_(\d{4}.\d{2}.\d{2})", filename)
searchResult = m.group(1)
print ("matcht result: %s" % searchResult)
dates = searchResult.split('.')
for date in dates:
print date
year = dates[0]
month = dates[1]
day = dates[2]
xingqi = datetime.datetime(int(year), int(month), int(day)).strftime("%w")
# replace to new filename
theReplacePart = '%s-%s-%s-%s' % (year,month,day,xingqi)
print 'the new filename is: %s' % theReplacePart
newfileName = re.sub("\d{4}.\d{2}.\d{2}", theReplacePart, filename)
print newfileName
6、
处理一些网址:
http://www.interoem.com/messageinfo.asp?id=35
http://3995503.com/class/class09/news_show.asp?id=14
http://lib.wzmc.edu.cn/news/onews.asp?id=769
http://www.zy-ls.com/alfx.asp?newsid=377&id=6
http://www.fincm.com/newslist.asp?id=415
deal with :.
http://www.interoem.com/
http://3995503.com/
http://lib.wzmc.edu.cn/
http://www.zy-ls.com/
# -*- coding:utf-8 -*-
import re
a1="http://www.interoem.com/messageinfo.asp?id=35"
a2="http://3995503.com/class/class09/news_show.asp?id=14"
a3="http://lib.wzmc.edu.cn/news/onews.asp?id=769"
a4="http://www.zy-ls.com/alfx.asp?newsid=377&id=6"
a5="http://www.fincm.com/newslist.asp?id=415"
l1 = []
l1.append(a1)
l1.append(a2)
l1.append(a3)
l1.append(a4)
l1.append(a5)
for i in range(5):
print l1[i]
print "*"*50
print " deal with......"
for i in range(5):
ret = re.sub(r"(http://.+?/).*",lambda x:x.group(1),l1[i])