python基础-正则表达式

常见的特殊字符:

  1. . ——(点)在默认模式,匹配除了换行的人一字符
  2. ^——(插入符合)。匹配字符串的开头,并且在MUlTILINE模式也匹配换行后的首个符号
  3. $——匹配字符串尾或者在字符串尾的换行符的前一个字符,在MUTLINE模式下也会匹配换行符之前的文本。
  4. *——  对他前面的正则式匹配0到任意次重复,尽量多的匹配字符串。
  5. +—— 对他前面的正则式匹配1到任意次重复
  6. ?——对他前面的正则式匹配0到1次重复。ab?会匹配'a'或者'ab'
  7. *?,+?,??——‘*’、‘+’、‘?’修饰符都是贪婪的;他们在字符串进行尽可能多的匹配。如果正则式 <.*> 希望找到 '<a> b <c>',它将会匹配整个字符串,而不仅是 '<a>'。在修饰符之后添加 ? 将使样式以 非贪婪`方式或者 :dfn:`最小 方式进行匹配; 尽量  的字符将会被匹配。 使用正则式 <.*?> 将会仅仅匹配 '<a>'
  8. {m}——对其之前的正则式指定匹配m个重复;少于m的话就会导致匹配失败
  9. {m,n}——对正则式进行m到n次匹配,在m和n之间取尽量多。
  10. {m,n}?——前一个修饰符的非贪婪模式,只匹配尽量少的字符次数。比如,对于 'aaaaaa', a{3,5} 匹配 5个 'a' ,而 a{3,5}? 只匹配3个 'a'
  11. \—— 转义特舒服(允许你匹配'*'、'?')
  12. []——用于表示一个字符集合。在一个集合中:
    1. 字符可以单独列出,比如[amk]匹配 'a', 'm', 或者 'k'
    2. 可以表示字符范围,通过用'-'将两个字符连起来。比如 [a-z] 将匹配任何小写ASCII字符, [0-5][0-9] 将匹配从 00 到 59 的两位数字, [0-9A-Fa-f] 将匹配任何十六进制数位。 如果 - 进行了转义 (比如 [a\-z])或者它的位置在首位或者末尾(如 [-a] 或 [a-]),它就只表示普通字符 '-'
    3. 特殊字符在集合中,失去它的特殊含义。比如 [(+*)] 只会匹配这几个文法字符 '(''+''*', or ')'
    4. 字符类如\w 或者 \S (如下定义) 在集合内可以接受,它们可以匹配的字符由 ASCII 或者 LOCALE 模式决定。
    5. 不在集合范围内的字符可以通过取反来进行匹配。比如 [^5] 将匹配所有字符,除了 '5', [^^] 将匹配所有字符,除了 '^'^ 如果不在集合首位,就没有特殊含义。
    6. 在集合内要匹配一个字符 ']',有两种方法,要么就在它之前加上反斜杠,要么就把它放到集合首位。比如, [()[\]{}] 和 []()[{}] 都可以匹配括号。
  13. |—— A|B,A和B可以式任意正则表达式,创建一个正则表达式,匹配A或者B。任意个正则表达式可以用'|'连接。
  14. (……)——(组合),匹配括号内的任意正则表达式,并标识出组合的开始和结尾。匹配完成后,组合的内容可以被获取,并可以在之后用\number转义序列进行再次匹配。

扩展标记法

(一个 '?' 跟随 '(' 并无含义)(?……)。

'?' 后面的第一个字符决定了这个构建采用什么样的语法。这种扩展通常并不创建新的组合; (?P<name>...) 是唯一的例外。 以下是目前支持的扩展。

  1. (?:…)

    正则括号的非捕获版本。 匹配在括号内的任何正则表达式,但该分组所匹配的子字符串 不能 在执行匹配后被获取或是之后在模式中被引用。

    >>> a = '123dd'
    >>> re.match('(?:\d+)', a).groups()
    ()
    >>> re.match('(\d+)', a).groups()
    ('123',)
    >>>

  2. (?P<name>…)

    (命名组合)类似正则组合,但是匹配到的子串组在外部是通过定义的 name 来获取的组合名必须是有效的Python标识符,并且每个组合名只能用一个正则表达式定义,只能定义一次。一个符号组合同样是一个数字组合,就像这个组合没有被命名一样。

  3. (?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'}

  4. (?#…)

    注释;里面的内容会被忽略。

    >>> re.match('(?P<data>\d+).*?(?#this is coment)', a).groupdict()
    {'data': '123'}
    >>>

  5. (?=…)

    匹配  的内容,但是并不消费样式的内容。这个叫做 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位呢?

  6. (?!…)

    匹配  不符合的情况。这个叫 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']

  7. (?<=…)

    匹配字符串的当前位置,它的前面匹配  的内容到当前位置。这叫:dfn:positive lookbehind assertion (正向后视断定)。 (?<=abc)def 会在 'abcdef' 中找到一个匹配,因为后视会往后看3个字符并检查是否包含匹配的样式。包含的匹配样式必须是定长的,意思就是 abc 或 a|b 是允许的,但是 a* 和 a{3,4} 不可以。注意以 positive lookbehind assertions 开始的样式,如 (?<=abc)def ,并不是从 a 开始搜索,而是从 d 往回看的。你可能更加愿意使用 search() 函数,而不是 match() 函数:

  8. (?<!…)

    匹配当前位置之前不是 ... 的样式。

  9. (?(id/name)yes-pattern|no-pattern)

    如果给定的 id 或 name 存在,将会尝试匹配 yes-pattern ,否则就尝试匹配 no-patternno-pattern 可选,也可以被忽略。比如, (<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$) 是一个email样式匹配,将匹配 '<user@host.com>' 或 'user@host.com' ,但不会匹配 '<user@host.com' ,也不会匹配 'user@host.com>'

常见的特殊序列:

  1. \number

    匹配数字代表的组合。每个括号是一个组合,组合从1开始编号。比如 (.+) \1 匹配 'the the' 或者 '55 55', 但不会匹配 'thethe' (注意组合后面的空格)。这个特殊序列只能用于匹配前面99个组合。如果 number 的第一个数位是0, 或者 number 是三个八进制数,它将不会被看作是一个组合,而是八进制的数字值。在 '[' 和 ']' 字符集合内,任何数字转义都被看作是字符。具体其他数字,待以后找到再更新

  2. \A

    只匹配字符串开始。

  3. \b

    匹配空字符串,但只在单词开始或结尾的位置。一个单词被定义为一个单词字符的序列。注意,通常 \b 定义为 \w 和 \W 字符之间,或者 \w 和字符串开始/结尾的边界, 意思就是 r'\bfoo\b' 匹配 'foo''foo.''(foo)''bar foo baz' 但不匹配 'foobar' 或者 'foo3'

  4. \B

    匹配空字符串,但  能在词的开头或者结尾。意思就是 r'py\B' 匹配 'python''py3''py2', 但不匹配 'py''py.', 或者 'py!'\B 是 \b 的取非,

  5. \d

    匹配任何Unicode十进制数

  6. \D

    匹配任何非十进制数字的字符

  7. \s

    匹配任何Unicode空白字符

  8. \S

    匹配任何非空白字符。

  9. \w

    匹配Unicode词语的字符

  10. \W

    匹配非单词字符的字符。

  11. \Z

    只匹配字符串尾。

模块内容

  1. re.compile(pattern,flags=0)。将正则表达式的样式编译为一个正则表达式 对象,可以用于匹配,通过这个对象的方法match(),search()以及其他。
    import re
    #序列
    
    prog = re.compile(pattern)
    result = prog.match(string)
    #等价于
    
    result = re.match(pattern, string)

  2. 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"

  3. 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'>

  4. re.fullmatch(pattern,string,flags=0)。如果整个string匹配到正则表达式样式,就返回一个相应的匹配对象。否则就返回一个None
  5. 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', '...', '', '', '']

  6. re.findall(pattern,string,flags=0)。对string返回一个不重复的pattern的匹配列表,string从左到右进行扫描,匹配按找到的顺序返回。如果样式里存在一到多个组,就返回一个组合列表;就是一个元组的列表。
  7. re.finditer(pattern,strig,flags=0)。pattern在string里所有的非重复匹配,返回一个迭代器iterator保存了匹配对象。string从左到右扫描,匹配按顺序排列。
  8. 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) #每次匹配一个数字,执行函数,获取替换后的值

  9. re.subn(pattern,repl,string,count=0,flags=0)。行为与sub()相同,但是返回一个元组(字符串,替换次数)
  10. re.escape(pattern)。转义pattern中的特殊字符。如果你想对任意可能包含正则表达式元字符的文本字符串进行匹配,他就是有用的。

匹配对象

match = re.search(pattern, string)
if match:
    process(match)
  1. Match.expand(template)。对 template 进行反斜杠转义替换并且返回,就像 sub() 方法中一样。
  2. 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')

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

  4. 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])

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值