python正则表达式

正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。—— 用于匹配字符串

import re  # 导入正则包

1. re.match方法:从字符串的起始位置匹配一个模式,如果起始位置匹配不成功的话则返回none。

re.match(pattern, string, flags=0)

import re
print(re.match('人才', '人才是每个公司争夺的目标').span())  # 在起始位置匹配,返回(起始位置,结束位置),不包括右边界 (0, 2)
print(re.match('公司', '人才是每个公司争夺的目标'))         # 不在起始位置匹配,返回None,相当于起始位置没有找到匹配的模式 None
print(re.match('人才', '人才是每个公司争夺的目标').end())   # 2   
line = "一向年光有限身。等闲离别易销魂。酒筵歌席莫辞频。满目山河空念远,落花风雨更伤春。不如怜取眼前人。"
matchObj = re.match( r'(.*)落花(.*?).*', line) # (.*)表示 '落花' 前面的为一个整体进行匹配,(.*?)表示出现(.*?)0次,因为后面的?表示惰性匹配
obj_group = matchObj.group()     #得到的是str不是元组
print(matchObj.group(0)) # 输出匹配到的整个串:一向年光有限身。等闲离别易销魂。酒筵歌席莫辞频。满目山河空念远,落花风雨更伤春。不如怜取眼前人。 ———— 原因是:最后的'.*'造成贪婪匹配模式,所以全部匹配得到
print(matchObj.group(1)) # 输出匹配到的第一个分组对应的子串:一向年光有限身。等闲离别易销魂。酒筵歌席莫辞频。满目山河空念远,
print(matchObj.group(2)) #输出匹配到的第二个分组对应的子串:空   ————原因是:只有用'()'包括的模式才能形成分组,而后一个分组因为'?'的原因形成惰性匹配,选择的是0次,所以为空
line = "一向年光有限身。等闲离别易销魂。酒筵歌席莫辞频。满目山河空念远,落花风雨更伤春。不如怜取眼前人。"
#matchObj = re.match( r'(.*)落花(.*?).*', line) # (.*)表示 '落花' 前面的为一个整体进行匹配,(.*?)表示出现(.*?)0次,因为后面的?表示惰性匹配
matchObj = re.match( r'(.*)落花(.+?).*', line)
print(matchObj.group(0)) #
print(matchObj.group(1)) #
print(matchObj.group(2)) #输出:风  ———— 此时第二个子串不再为空,而是'风',因为'+'是至少匹配1次,在惰性匹配模式下取最小值1,而之前的'*'是匹配0次或多次,也就是最少次数是0次
line = "一向年光有限身。等闲离别易销魂。酒筵歌席莫辞频。满目山河空念远,落花风雨更伤春。不如怜取眼前人。"
matchObj = re.match( r'(.*)落花(.+?)', line)
print(matchObj.groups()) # ('一向年光有限身。等闲离别易销魂。酒筵歌席莫辞频。满目山河空念远,', '风')  ———— 得到一个元组:由匹配到的字符串分组构成
print(matchObj.group(0)) # 一向年光有限身。等闲离别易销魂。酒筵歌席莫辞频。满目山河空念远,落花风    ———— 原因是:去掉了末尾的'.*',因此,无法匹配到后面的文字了
print(matchObj.group(1)) # 一向年光有限身。等闲离别易销魂。酒筵歌席莫辞频。满目山河空念远,
print(matchObj.group(2)) #

# (.*) 第一个匹配分组,.* 代表匹配除换行符之外的所有字符。
#  (.*?) 第二个匹配分组,.*? 后面多个问号,代表非贪婪模式,即只匹配符合条件的最少次数  —— (即0)
#  后面的一个 .* 没有括号包围,不属于分组,所以结果不计入分组结果。

 

2. re.search方法:扫描整个字符串并返回第一个成功的匹配

re.search(pattern, string, flags=0)

print(re.search('人才', '人才是每个公司争夺的目标').span())  # 在起始位置匹配 (0, 2),均不包含右边界
print(re.search('公司', '人才是每个公司争夺的目标').span())  # 不在起始位置匹配 (5, 7)
# 总结 ---- group,groups得到的是字符串的值;其他的得到的是索引位置
#
============================================================================= # *注:match() 和 search()匹配成功,会得到match object对象,该对象有以下方法: # group() 返回被 RE模式 匹配的字符串 # start() 返回匹配开始的位置 # end() 返回匹配结束的位置,不包括该右边界 # span() 返回一个元组,包含匹配 (开始,结束) 的位置, 不包括右边界 # group() 返回re整体匹配的字符串,可以一次输入多个组号,得到对应组号匹配的字符串 # groups() 返回一个元组,该元组由匹配得到的分组字符串构成 # =============================================================================
# ================================= python正则表达式的flags解释 ============================================
# flags : 可选,表示匹配模式,比如忽略大小写,多行模式等,具体参数主要有:
# re.I 忽略大小写
# re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境
# re.M 多行模式
# re.S 即为 . 并且包括换行符在内的任意字符 (因为单纯的'.'不包括换行符)
# re.U 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库
# re.X 为了增加可读性,忽略空格和 # 后面的注释
# ========================================================================================================

3. re.sub方法:用于替换字符串中检索到的匹配项

re.sub(pattern, repl, string, count=0, flags=0) 

#参数:
#pattern : 正则表达式
#repl : 替换的字符串,可使用一个函数
#string : 要被查找替换的字符串
#count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。

# repl为字符串
test_str = "你是-人间-四月天 #林徽因"
# 删除字符串的注释 
test_title = re.sub(r'#.*$', "", test_str)  
print("诗名是: ", test_title) # 输出'诗名是:  你是-人间-四月天 '末尾有一个空格
 
# 删除(-)
test_title = re.sub(r'-', "", test_str)
print("诗名是 : ", test_title)  #输出'诗名是 :  你是人间四月天 #林徽因'
# repl为函数
def trans(match_str):      #match_str 也就是re.sub函数传过来的第一个参数 '(?P<value>-)'
    value = match_str.group('value')  #value是前面设置的参数标识符,用于取该标识符对应的值,这个值就是用于修改
    trans_value = value.replace('-','**') #修改得到的值,将value中的'-'修改为'**',然后整体作为修改值,只是这里刚好value也是'-'
    return trans_value
 
s = '你是-人间-四月天'
print(re.sub('(?P<value>-)', trans, s))  #trans函数作为参数,<value>用于显式的修改参数,返回的是函数处理过的值,输出:'你是**人间**四月天'

# 可以分步看看:
(?P<value>...) 表示先匹配...,并用value进行标识,上述 (?P<value>-) 表示先匹配 '-',然后用<value>进行标识,即 {'value':'-'}

st = re.search('(?P<value>-)', s)   # re.search返回的是第一个成功的匹配,re.sub却可以对多个进行操作
print(st)
print(st.group())   # '-'
print(st.groupdict())  # {'value':'-'},分组得到的值
print(st.group('value'))  # '-'

# 其实整个也就是一行:s.replace('-','**') 的结果,只不过使用正则的话规则可以更多更复杂

 例如,value值不是'-'时:

import re

def trans(match_str):
    print(match_str)
    value = match_str.group('value')  #前面设置的参数标识符
    print(value)
    trans_value = value.replace('-','**') #修改得到的值,将其中的'-'修改为'**'
    print(trans_value)
    return trans_value
 
s = '你是-人间-四月天'
print(re.sub('(?P<value>-四月)', trans, s))   # 结果是:你是-人间**四月天
# (?P<value>...) 表示按照...进行分组,得到value标识的值
# 上述 (?P<value>-四月) 表示将 '-四月' 用<value>进行标识,即 {'value':'-四月'}

st = re.search('(?P<value>-四月)', s)
print(st)
print(st.group())
print(st.groupdict())
print(st.group('value'))

4. re.compile方法:用于编译正则表达式,生成一个正则表达式模式用于判断,可以用于match()、search()、findall()

pattern = re.compile(r'\d+')                    # 匹配至少一个数字,此时match()表示从开头进行匹配
test = pattern.match('笛子的五线谱中有:1234567,一共7个基本音')        # 查找头部,没有匹配,None
print(test)
test = pattern.match('笛子的五线谱中有:1234567,一共7个基本音', 2, 10) # 将索引为2的位置作为开头进行匹配,匹配失败,None
print(test)
test = pattern.match('笛子的五线谱中有:1234567,一共7个基本音', 9, 20) # 从'9'的位置开始匹配,正好匹配,返回值不包含右边界
print(test)                                        # 查找成功则返回一个 match 对象

print(test.group(0))  # 输出:1234567
print(test.start(0))  # 输出:9
print(test.end(0))    # 输出:16
print(test.span(0))   # 输出:(9, 16)
pattern = re.compile(r'([a-z]+) ([a-z]+)', re.I)   # re.I 表示忽略大小写,分组表示以()包围的模式,没有括号得到的不是分组
test = pattern.match('when are you leaving for canada?')
print(test.group(0))  #str串 when are  # 返回匹配成功的整个子串,遇到are之后的空格就停止,因为空格不在([a-z]+)模式之中
print(test.group(1))  # when                      # 返回第一个分组匹配成功的子串
print(test.groups())  #形成元组 ('when', 'are'), # 等价于 (test.group(1), test.group(2), ...)

print(test.span(0))                             # 返回匹配成功的整个子串的索引 (0, 8)
print(test.span(1))                             # 返回第一个分组匹配成功的子串索引 (0, 4)
print(test.span(2))                             # 返回第二个分组匹配成功的子串索引 (5, 8)

 

5. findall方法:在字符串中找到模式匹配的所有子串,并返回一个列表,若没有找到匹配的就返回空列表

findall(string[, pos[, endpos]])

pattern = re.compile(r'\d+')   # 查找数字
result1 = pattern.findall('笛子的五线谱中有:1234567,一共7个基本音') #查找字符串中的所有数字
print(result1) # ['1234567', '7']

 

6. finditer方法:和 findall 类似,在字符串中找到模式匹配的所有子串,并把它们作为一个迭代器返回

iter_test = re.finditer(r'\d+','笛子的五线谱中有:1234567,一共7个基本音') 
for ss in iter_test: 
    print(ss.group()) #迭代器,每次得到一个
# 1234567
# 7

 

7. split方法:按照匹配的子串将字符串分割后返回列表

re.split(pattern, string[, maxsplit=0, flags=0])

#pattern —— 匹配的正则表达式
#string —— 要匹配的字符串
#maxsplit —— 分隔次数,maxsplit=1 表示分隔1次,默认为 0,不限制次数
#flags —— 标志位

print(re.split('\W+', 'when are you leaving for canada?'))
# 输出:['when', 'are', 'you', 'leaving', 'for', 'canada', '']
# \W : 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'
# 用找到的串作为分隔进行划分,例子中的就是:空格和问号
print(re.split('\W+', 'when are you leaving for canada?', 1)) 
# ['when', 'are you leaving for canada?']

 

8. '(?P...)' 分组匹配

test_str = '3301232005xxxxxxxx'
test_ = re.search('(?P<province>\d{3})(?P<city>\d{3})(?P<born_year>\d{4})', test_str) # <>内的值作为标识所取的值
print(test_.groupdict())            # 将匹配结果直接转为字典
# 输出:{'province': '330', 'city': '123', 'born_year': '2005'}

 

9. 注意事项

#(1).字母和数字会直接匹配自身
#(2).多数字母和数字前加一个反斜杠时会有不同特殊含义。
#(3).标点符号只有被转义时才可以匹配自身。
#(4).反斜杠本身需要使用反斜杠转义,即'\\'。
#(5).由于正则表达式通常都会包含反斜杠'\',所以最好使用原始字符串模式(如 r'\t',等价于 '\\t')来匹配相应的字符。

 

10. 特殊元素

#    ^ —— 匹配字符串的开头
#    $ —— 匹配字符串的末尾。
#    . —— 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。
#    [...] —— 用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k'
#    [^...] —— 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。
#    re* —— 匹配0个或多个的表达式。
#    re+ —— 匹配1个或多个的表达式。
#    re? —— 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
#    re{ n} —— 精确匹配 n 个前面表达式。例如, o{2} 不能匹配 "Bob" 中的 "o",但是能匹配 "food" 中的两个 o。
#    re{ n,} —— 匹配 n 个前面表达式。例如, o{2,} 不能匹配"Bob"中的"o",但能匹配 "foooood"中的所有 o。"o{1,}" 等价于 "o+"。"o{0,}" 则等价于 "o*"。
#    re{ n, m} —— 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
#    a| b —— 匹配a或b
#    (re) —— 对正则表达式分组并记住匹配的文本,被括起来的表达式将作为分组,从表达式左边开始每遇到一个分组的左括号“(”,编号+1.分组表达式作为一个整体,可以后接数量词。表达式中的|仅在该组中有效。
#    (?imx) —— 正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。
#    (?-imx) —— 正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。
#    (?: re) —— 类似 (...), 但是不表示一个组
#    (?imx: re) —— 在括号中使用i, m, 或 x 可选标志
#    (?-imx: re) —— 在括号中不使用i, m, 或 x 可选标志
#    (?#...) —— 注释.
#    (?= re) —— 前向肯定界定符。如果所含正则表达式,以 ... 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边。
#    (?! re) —— 前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功
#    (?> re) —— 匹配的独立模式,省去回溯。
#    \w —— 匹配字母数字及下划线
#    \W —— 匹配非字母数字及下划线
#    \s —— 匹配任意空白字符,等价于 [\t\n\r\f].
#    \S —— 匹配任意非空字符
#    \d —— 匹配任意数字,等价于 [0-9].
#    \D —— 匹配任意非数字
#    \A —— 匹配字符串开始
#    \Z —— 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。
#    \z —— 匹配字符串结束
#    \G —— 匹配最后匹配完成的位置。
#    \b —— 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
#    \B —— 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
#    \n, \t, 等. —— 匹配一个换行符。匹配一个制表符。等
#    \1...\9 —— 匹配第n个分组的内容。
#    \10 —— 匹配第n个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式。
#    实例 —— 描述
#    [Pp]ython  —— 匹配 "Python" 或 "python"
#    rub[ye] —— 匹配 "ruby" 或 "rube"
#    [aeiou] —— 匹配中括号内的任意一个字母
#    [0-9] —— 匹配任何数字。类似于 [0123456789]
#    [a-z] —— 匹配任何小写字母
#    [A-Z] —— 匹配任何大写字母
#    [a-zA-Z0-9] —— 匹配任何字母及数字
#    [^aeiou] —— 除了aeiou字母以外的所有字符 
#    [^0-9] —— 匹配除了数字外的字符 

#    . —— 匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用象 '[.\n]' 的模式。
#    \d —— 匹配一个数字字符。等价于 [0-9]。
#    \D  —— 匹配一个非数字字符。等价于 [^0-9]。
#    \s —— 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
#    \S  —— 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
#    \w —— 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。
#    \W —— 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。

 

附加:贪婪匹配与非贪婪匹配

*?,+?,??,{m,n}?    前面单独的*,+,?等都是属于贪婪匹配,也就是尽可能匹配多的次数,后面加'?'号就会使其变成非贪婪匹配(惰性匹配)

 

参考:
https://www.cnblogs.com/tina-python/p/5508402.html
https://www.runoob.com/python/python-reg-expressions.html

# 欢迎交流

posted on 2019-12-04 22:03  落日峡谷  阅读(479)  评论(0编辑  收藏  举报

导航