第二十二篇 正在表达式 re模块
re模块******
就本质而言,正则表达式时一种小型的,高度专业化的编程语言,在python里,它内嵌在python中,并通过re模块实现。正则表达式模式被编译成一系列的字节码。然后用C编写的匹配引擎执行。
正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。
re 模块使 Python 语言拥有全部的正则表达式功能。
说白了,正则就是用来处理字符串的。
简言之,正则就是给字符串进行模糊匹配
正则的用途:
1. 模糊匹配 2. 应用场景如:一个文本里存了一堆身份证号,要找到北京市的且1990年后出生的人
这个例子可以不用正则也能实现,但是用正则实现更简单
举例正则就可以这么实现:
^110.......1990 + * 3. 还有一些场景,不用正则是实现不了。
字符串匹配:普通字符、元字符(特殊符号)。
1. 普通字符:大多数字符和字母都会自身匹配
hello = 'nihaoa alex teacher' # 之前也学过其他的处理字符串的方法,比如find,split,但是这些方法都是精确查找,完全匹配的方法,如果想实现模糊查找就完不成目标了,然而正则就可以解决这个问题。
print(hello.find('alex'))
2. 元字符:. ^ $ * + ? {} [] () | \
元字符给我们提供了很多模糊匹配的可能。
学正则其实就学这些元字符和6个方法。
findall(pattern, string, flags=0): pattern表示匹配的规则,string表示要在哪个字符串里进行匹配。
findall()会把所有匹配的结果都拿出来。
- 第一个元字符 点 .
点:是通配符,除了换行符\n不能替代之外,其他什么都可以代替,如数字,字母,特殊字符等等。
记住:一个点,只能代表一个字符,不能代表多个字符。
import re
hello = 'nihaoa alex teacher,a24x,a234x' # 匹配出以a开头,以x结尾的字符
# 记住:一个点,只能代表一个字符,不能代表多个字符。
print(re.findall('a..x', hello)) # ['alex', 'a24x']
print(re.findall('a...x', hello)) # ['a234x'] # 如果有多个字符需要用.匹配,怎么简写呢?
[答】这个问题涉及到重复的问题,关于重复后面会有四个元字符来满足这个简写的功能。分别是 * + ? {}
- 第二个元字符:^
尖角号(^):表示以什么开头的意思。匹配字符串的开头,开头能匹配上,返回结果;开头匹配不上,就终止,返回空列表。
import re
hello1 = 'nihaoa alex teacher,a24x,a234x'
hello2 = 'alexawwxteacher,a24x,a234x' # ^:匹配以 某字符串 开头。
# 意思是,要去匹配hello1 和 hello2这两个字符串的开头,看他的开头是不是以 "a..x" 这样的四个字符串开头的
# 能匹配到,就返回匹配结果;没有匹配到就返回空列表
print(re.findall('^a..x', hello1)) # []
print(re.findall('^a..x', hello2)) # ['alex']
- 第三个元字符:$
美元符($):表示以什么字符串结尾的意思。匹配字符串的末尾。
import re
hello1 = 'nihaoa alex teacher,a24x,a234x'
hello2 = 'alexawwxteacher,a24x,a234x'
hello3 = 'alexawwxteacher,a24x,a234x$' # $: 匹配以 某字符串结尾
# 意思是,要去匹配hello1 和 hello2这两个字符串的结尾,看他们的结尾是不是以 "a...x" 这样的五个字符串结尾的 print(re.findall('a..x$', hello1)) # []
print(re.findall('a...x$', hello2)) # ['a234x']
print(re.findall('a...x$', hello3)) # []
- 第四个元字符:*
*是按照星号紧挨着的字符去重复, 重复范围是0到无穷次。匹配的时候是按照最多的来匹配
import re hello = 'assddjkfdsdddddsdfdadffffffddd' # * 是匹配0到无穷次的内容
print(re.findall('d*', hello))
# 结果
['', '', '', 'dd', '', '', '', 'd', '', 'ddddd', '', 'd', '', 'd', '', 'd', '', '', '', '', '', '', 'ddd', ''] # 预期的结果好像与我们的想想不一样,为什么?
# 【答】因为 * 的匹配范围是[0,无穷大),所以第一个字符是a, 也算是匹配上了。 #上面的例子并不好,换一个
import re
hello = 'dddfjakffdjkfddfderqerqeddddd' print(re.findall('^d*', hello))
# 结果
['ddd'] # 与* 对应的有一个元字符,是 + 号
- 第五个元字符:+
+ 号匹配的范围是[1,无穷次),意思是匹配字符至少得有一个。
# 这个被匹配字符串都有x,看不出区别
print(re.findall("alex*", "asdhfalexxxx")) # ['alexxxx']
print(re.findall("alex+", "asdhfalexxxx")) # ['alexxxx'] # 下面两个例子,被匹配字符串没有x,就能看出区别了 print(re.findall("alex*", "asdhfale"))
# 结果
['ale']
# 解释:因为*的匹配范围是[0,无穷次),即使没有x,也算是匹配上了 print(re.findall("alex+", "asdhfale"))
# 结果
[] # 解释:因为 + 的匹配范围是[1,无穷次),+ 号紧挨着的字符,在被匹配字符里必须至少有一个,才能匹配上
- 第六个元字符:?
?的匹配范围是[0,1],表示它可以匹配0次,或1次,只有这两个值。
print(re.findall("alex?", "asdhfalexxxx")) # ['alex']
print(re.findall("alex?", "asdhfale")) # ['ale']
- 第七个元字符:{}
{}的匹配范围是你自己定义的。
{0,}:相当于 *
{1,}:相当于 +
{0,1}:相当于 ?
{6}:相当于重复6次
{1,6}:可以重复1,2,3,4,5,6里面的任何一个
print(re.findall("alex{0,}", "asdhfalexxxx")) # ['alexxxx'] print(re.findall("alex{1,}", "asdhfalexxxx")) # ['alexxxx'] print(re.findall("alex{0,1}", "asdhfalexxxx")) # ['alex']
print(re.findall("alex{0,1}", "asdhfale")) # ['ale'] # 代表重复x6次
print(re.findall("alex{6}", "asdhfalexxxx")) # []
print(re.findall("alex{6}", "asdhfalexxxxxxxx")) # ['alexxxxxx']
print(re.findall("alex{1,6}", "asdhfalexxxxxxxxxx")) # ['alexxxxxx']
再看几个例子,总结一下
print(re.findall("a..in", "helloalvin"))
# ['alvin'] print(re.findall("^a...n", "alvinhelloalvin"))
# ['alvin'] print(re.findall("a...n$", "alvinhelloalvin"))
# ['alvin'] print(re.findall("abc*", "abcccc")) # 贪婪匹配[0,+∞)
# ['abcccc'] print(re.findall("abc+", "abcccc")) # 贪婪匹配[1,+∞)
# ['abcccc'] print(re.findall("abc?", "abcccc")) # 匹配[0,1]
# ['abc'] print(re.findall("abc{1,3}", "abccc")) #匹配[1,4]
# ['abccc']
- 惰性匹配
注意:
有 * + ? 的都是贪婪匹配,也就就是尽可能多的匹配;
但是如果在 * +的后面加?可以使其变成惰性匹配,所谓惰性匹配,就是按照最少的去匹配
print(re.findall("alex*?", "helloalvinalexxxxxxx"))
# ['ale']
# 解释:* 匹配范围是[0,+∞), 所以匹配上0次就可以算匹配成功了,*可以代表0了,所以匹配到ale就行了,后面就不匹配了 print(re.findall("alex+?", "helloalvinalexxxxxxx"))
# ['alex']
# 解释:+ 匹配范围是[1,+∞), 所以匹配上1次就可以算匹配成功了,+可以代表1了,所以匹配到alex就行了,后面的x就不匹配了
- 第八个元字符:[ ] (********************的重要)
[ ]的学名叫字符集,[ ]字符集的作用: 或的作用
import re '''
[]字符集的第1个作用:或的作用
'''
print(re.findall("x[yz]", "x"))
# [] print(re.findall("x[yz]", "xy"))
# ['xy'] print(re.findall("x[yz]", "xyuuu"))
# ['xy'] print(re.findall("x[yz]", "xyuuxzuu"))
# ['xy', 'xz'] print(re.findall("x[yz]", "xzzzyyzz"))
# ['xz'] print(re.findall("x[yz]p", "xyzzpxzyzzp"))
# [] print(re.findall("x[yz]p", "xyzzpxzpyzz"))
# ['xzp'] print(re.findall("x[yz]p", "xypuuxzpuu"))
# ['xyp', 'xzp'] print(re.findall("x[y,z]p", "xypuuxzpuu"))
# ['xyp', 'xzp'] # []里面加个逗号(,), 没有特殊作用,与y z一样,就是个普通符合,构成了三种可以匹配的组合:xyp, x,p xzp
print(re.findall("x[y,z]p", "xypuuxzpuu,pu"))
# ['xyp', 'xzp']
print(re.findall("x[y,z]p", "xypuuxzpuux,pu"))
# ['xyp', 'xzp', 'x,p']
1. [ ] 字符集里有特殊功能的特殊符号: -
- 的特殊意义:表示范围。
[ ] 字符集只有三个符号 - ^ \ 有特殊意义,其他符号都没有特殊意义。
# 记住:[]里面没有特殊符号,除了 - ^ \
print(re.findall("q[a*z]", "kssadfkq"))
# [] 匹配不到,因为[]里至少还有一个字母需要匹配 print(re.findall("q[a*z]", "kssadfkqaaa"))
# ['qa'] 前面讲过,*代表重复,但是放到[]字符集里,她就是个普通字符,没有其他特殊意义,构造三种组合 qa q* qz print(re.findall("q[a*z]", "kssadfkqaaaq**"))
# ['qa', 'q*'] # 在[] 有特殊符号 -
# 特殊意义:- 代表着范围,也就是a-z # 所以,下面的q后面随便跟一个a~z的字符,就能匹配到
print(re.findall("q[a-z]", "qu"))
# ['qu'] # [a-z]也只代表一个字符,也就是可以是qa,qb,qc,qd,qe......qz等26个组合, 只要匹配到一个就停止匹配了
print(re.findall("q[a-z]", "quiuo"))
# ['qu'] # [a-z]*:表示a-z 26个字母可以重复
print(re.findall("q[a-z]*", "quiuofdfafad"))
# ['quiuofdfafad'] print(re.findall("q[a-z]*", "quiuofdfafad9"))
# ['quiuofdfafad'] #
print(re.findall("q[0-9]*", "quiuofdffafad9"))
# ['q']
# 解释:
# 对这个结果别意外, 虽然"quiuofdffafad9" q后面没有紧挨着的数字,
# 但是 因为*的匹配范围是[0,+∞), 就是没匹配到的但也算匹配上了。所以结果是q print(re.findall("q[0-9]*", "quiuofdffqafad9"))
# ['q', 'q'] print(re.findall("q[0-9]*", "quiuq89ofdffqafad9"))
# ['q', 'q89', 'q'] print(re.findall("q[A-Z]*", "quiuq89ofdffqafad9"))
# ['q', 'q', 'q']
2. [ ] 字符集里有特殊功能的特殊符号: ^
特别要注意的:[ ]字符集里的^ 与 上面讲的放在开头的^ 有天大的区别。
放在[ ]里的^ 的特殊意义:表示 非。 举例 q[^a-z] 表示 只要不是a-z里的都能匹配上
print(re.findall("q[^a-z]", "q123"))
# ['q1']
print(re.findall("q[^a-z]", "qab"))
# [] print(re.findall("q[^a-z]", "qz"))
# [] print(re.findall("q[^a-z]", "quiuq89ofdffqafad9"))
# ['q8'] # 需求:怎么把(2-1)提出来?
print(re.findall("\([^()]*\)", "12+(34*6+2-5*(2-1))"))
# ['(2-1)'] # 解释:
'''
元字符()是有特使意义的,如果要把()变成普通字符,要加个 斜杠\
1. \( 表示以(开头, \) 表示以 )结尾
2. 中间得是没有()的, 才代表着最里层。所以要写给字符集[ ],字符集里写[^()]:表示非(),且是一个字符
3. 第一步和第二步已经匹配出来了以(开头,以)结尾,但是()里面有多少字符,都是啥你并不知道,所以要在[]外面加个 * 统配
'''
3. [ ] 字符集里有特殊功能的特殊符号: \ 转义付
\ 的特殊意义,也是最重要的一个:
(1)能让有功能的符号变为没功能(\后跟元字符,去重特殊功能, 比如 \.)
(2)也能让没功能的符号变为有功能(\后跟普通字符,实现特殊功能,比如 \d)
#### \ 让没有意义的字符,变得有意义#### \d: 匹配任何十进制数, \d 代表 [0 -9 ] 的任何一个数字,只代表一个数字,相当于 [0-9]
\d+:匹配任何十进制数, \d+只代表多个数字组合在一起,相当于 [0-9]+
\D:匹配任何非数字字符, 相当于 [^0 - 9] print(re.findall("\d", "12+(34*6+2-5*(2-1))"))
# ['1', '2', '3', '4', '6', '2', '5', '2', '1'] print(re.findall("\d+", "12+(34*6+2-5*(2-1))"))
# ['12', '34', '6', '2', '5', '2', '1'] print(re.findall("\D+", "12+(34*6+2-5*(2-1))"))
# ['+(', '*', '+', '-', '*(', '-', '))'] \s: 匹配任何空白字符,相当于[\t\n\r\f\v]
\S: 匹配任何非恐怖字符,相当于[^\t\n\r\f\v]
\S+:匹配任何非恐怖字符,字符可以组合在一起 # 可以取出空格
print(re.findall("\s", "hello wor ld!"))
# [' ', ' ', ' '] print(re.findall("\s+", "hello world!"))
# [' '] print(re.findall("\S", "hello world!"))
# ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', '!']
print(re.findall("\S+", "hello world!"))
# ['hello', 'world!'] \w:匹配任何字母数字字符及下划线,相当于[a-zA-Z0-9_]
\W:匹配任何非字母数字字符及非下划线,相当于[^a-zA-Z0-9_] print(re.findall("\w", "[a-zA-Z0-9_]"))
# ['a', 'z', 'A', 'Z', '0', '9', '_']
print(re.findall("\w+", "[a-zA-Z0-9_]"))
# ['a', 'zA', 'Z0', '9_'] print(re.findall("\W", "[a-zA-+Z0-9_]"))
# ['[', '-', '-', '+', '-', ']'] print(re.findall("\W+", "[a-zA-+Z0-9_]"))
# ['[', '-', '-+', '-', ']'] \b:匹配一个特殊字符边界,比如 空格,&,#,@等等 # 需求:匹配出一个 I
print(re.findall("I\b", "I am LIST"))
# [] # 分析:上面拿不到,看I前后的特点,是I后有个空格,所以就可以用\b来匹配这个特殊符号空格了。
print(re.findall("I\b", "I am LIST"))
# [ ]
# 为什么还是不行呢? # 匹配规则前面加个r,代表转义, r表示原生字符串,表示在Python这一层,里面的任何内容都不做转义了,就把\b代表空格的意思传给了re模块
print(re.findall(r"I\b", "I am LIST"))
# [ ] # 匹配规则里加个\,也代表转义
# 在re模块匹配规则里,\\:等于去掉了\的特殊意义,就把\变成了要给普通的\字符,然后\b,就表示空格,传给re模块就是真正的空格了。
print(re.findall("I\\b", "I am LIST")) # 问题:上面\b已经都代表了空格,为啥还匹配不到呢?
# 【答】1. 如果只在re模块里,\b就代表了空格,是没问题的
# 2. 但是,程序是要用Python解释器执行的,而\b在Python 解释器里是有特殊意义的,当Python执行到print(re.findall("I\b", "I am LIST"))的时候,\b就被python解释器解释成了Python的意思,而传给re模块就不再是空格了
# 3. 所以,要通过 r"I\b"告诉python解释器不转义 或者 "I\\b" 来转义
# 所以,总结一下,你想匹配的内容,是需要re模块能认识的才行。 # 再看个例子
# 需求:匹配一个 c\l, \是就是一个普通的字符
# 想匹配的是 c\l,首先在正则里面, 得是c\\l, 通过多加一个\把另一个正则
# 里的转义\ 进行转义,转义成一个普通的\.
# 然而,程序是通过Python解释器解释的,那么也就是说python执行完该行程序#
# 后,需要给re里面传个c\\l, 然后re才能 将c\\l 转义成 c\l这个普通字符。
# 所以re.findall("c\\l"),python解释器只给re传了一个\进入,即c\l是传给了re,
# 而实际上,re需要\\,所以,re.findall("c\\\\l"),python解释器自己会先转义一下剩下了c\\l,这个再传给re,re里就变成了 c\\l, 然后re自己再转义一次,就变成了最终想要的 c\l的结果。 print(re.findall("c\\\\l","nihaos c\l"))
# ['c\\l'] # 当然,这么写实在是太麻烦了,可以通过再前面加个r,告诉python解释器,这个不需要转义,然后直接传给re的就是c\\l了
print(re.findall(r"c\\l","nihaos c\l"))
# ['c\\l'] # 当然,这里返回结果又有疑问了,我们要匹配的是c\l,可以返回结果是["c\\l"]呀,不对啊。
【答】其实,这是对的,因为匹配是再re里进行的,匹配成功并返回结果给python解释器这一层了,而传给python解释器又要进行python解释器这一次的转义,所以,看到的就是["c\\l"].
重要的不是你看到了几个\,重要的是匹配成功了。
#### \ 让有意义的字符变得没意义#### # 下面的匹配规则里, . 可以代表任意字符,下面是匹配结果
print(re.findall("www.baidu", "www.baidu")) # ['www.baidu']
print(re.findall("www.baidu", "wwwobaidu")) # ['wwwobaidu']
print(re.findall("www.baidu", "www/baidu")) # ['www/baidu']
print(re.findall("www.baidu", "www\nbaidu")) # [] 因为. 不能匹配换行符 # 如果在匹配规则里的.前面加个\,即 \. 就让.代表任意字符的功能消失掉了,这个. 此时就代表一个普通的字符.
print(re.findall("www\.baidu", "www.baidu"))
# ['www.baidu'] # 所以这个是可以匹配出来的,因为被匹配字符串里有个. ,与匹配规则一致 # 而下面三个就无法匹配上了
print(re.findall("www\.baidu", "wwwobaidu")) # []
print(re.findall("www\.baidu", "www/baidu")) # []
print(re.findall("www\.baidu", "www\nbaidu")) # [] print(re.findall("www*baidu", "www*baidu")) # []
print(re.findall("www\*baidu", "www*baidu")) # ['www*baidu']
- 第九个元字符:|
| 管道符表示 或的意思,或 | 左边的一部分,或者|右边的一部分
[ ] 字符集 也有或的作用
print(re.findall(r"ka|b", "sdjkasf")) # ['ka']
print(re.findall(r"ka|b", "sdjka|bsf")) # ['ka', 'b']
print(re.findall(r"ka|bc", "sdjka|bsf")) # ['ka']
print(re.findall(r"ka|bc", "sdjka|bcsf")) # ['ka', 'bc']
- 第十个元字符:()
()表示分组
再学分组之前,先看下search()方法和findall()方法的比较
# search()方法:
# 1. 在匹配字符串里,只要要到一个符合的结果就返回了,不会再继续往后找了;而且search()方法返回但是一个对象;
# 2. 如果没有匹配到,就什么都不返回
# 3. 返回的对象里,group()方法可以提取出返回的值 # findall()方法:是把字符串里所有匹配到的字符串都放到一个列表里。 print(re.findall("\d+", "dfjad;fj123fjdfk33dfad3e23"))
# ['123', '33', '3', '23'] print(re.search("\d{5}", "dfjad;fj123fjdfk33dfad3e23"))
# None print(re.search("\d+", "dfjad;fj123fjdfk33dfad3e23"))
# <_sre.SRE_Match object; span=(8, 11), match='123'> # 怎么从返回对象里提取出想要的值呢?
print(re.search("\d+", "dfjad;fj123fjdfk33dfad3e23").group())
#
下面是分组的内容:
#
print(re.findall("abc", "abcccc")) # ['abc']
print(re.findall("abc+", "abcccc")) # ['abcccc']
print(re.findall(r"abc+", "abcccc")) # ['abcccc'] # 无名分组
# 上面的+重复的只是紧挨着+的c这一个字符
# 但是现在想要让+重复abc这一个整体,就需要用到()进行分组了
print(re.findall(r"(abc)+", "abcccc")) # ['abc']
print(re.findall(r"(abc)", "abcccc")) # ['abc'] print(re.findall(r"(abc)+", "abcabcabcc")) # ['abc']
print(re.findall("(abc)+", "abcabcabcc")) # ['abc']
# 其实匹配出的结果是['abcabdabc'],但因为是分组的,默认优先只展示分组里的,而分组里只有一个abc,所以显示了一个有括号的一部分:['abc']
# 只出现一个abc,是因为分组了,默认只显示组内匹配的内容
print(re.findall("(?:abc)+", "abcabcabcc")) # ['abcabcabcc"] 去除了默认显示分组匹配的内容 print(re.findall("(abc)", "abcabcabcc")) # ['abc', 'abc', 'abc']
print(re.findall("(abc)", "abcdefabcessabcc")) # ['abc', 'abc', 'abc']
print(re.findall("(abc)", "abcabcabcc")) # ['abc', 'abc', 'abc'] #有名分组
# 有名分组
print(re.findall("(?P<name>\w+)","abcccc")) # ['abcccc']
print(re.findall("(?P<name>[a-z]+)","abcccc")) # ['abcccc']
print(re.findall("(?P<name>[a-z]+)","alex36wusir34xialv33")) #['alex', 'wusir', 'xialv']
print(re.findall("(?P<name>\d+)","alex36wusir34xialv33")) # ['36', '34', '33'] # 下面这个是固定写法
# 匹配规则里"(?P<name>真正的匹配内容)" print(re.search("(?P<name>[a-z]+)","alex36wusir34xialv33").group()) # alex
print(re.search("(?P<name>\d+)","alex36wusir34xialv33").group()) #
print(re.search("(?P<name>[a-z]+)\d+","alex36wusir34xialv33").group()) # alex36 # 去掉上面的?P<name>,结果并没有什么不同
print(re.search("[a-z]+","alex36wusir34xialv33").group()) # alex
print(re.search("\d+","alex36wusir34xialv33").group()) #
print(re.search("[a-z]+\d+","alex36wusir34xialv33").group()) # alex36 # 那似乎?P<name>没有什么作用?
# 对于真正要匹配的内容,的确是没有什么作用
# 那?P<name>的作用到底在哪里?
# [答]是相当于把这个内容做了要给分组,每个匹配到的内容都可以通过name去拿了
print(re.search("(?P<name>[a-z]+)\d+","alex36wusir34xialv33").group()) # alex36 # 上面是已经分组了,按照name分组了,匹配到的真正的信息是alex36,
# 但如果只想取到alex,也就是只想拿到取出结果的name,也就是只想取分组的内容
print(re.search("(?P<name>[a-z]+)\d+","alex36wusir34xialv33").group("name")) # alex print(re.search("(?P<name>[a-z]+)(?P<age>\d+)","alex36wusir34xialv33").group()) # alex36 print(re.search("(?P<name>[a-z]+)(?P<age>\d+)","alex36wusir34xialv33").group("age")) #
re模块下常用的方法
findall() 和 search()方法上面已经有例子了,不再重复
- re.findall(): 返回所有满足匹配条件的结果,放在列表里
- re.search() : 函数会在字符串内查找模式匹配,直到找到第一个匹配然后返回一个对象
- 意义: 比如计算器程序的时候就比较有用
- re.search().group():通过调用group()方法可以提取出匹配的字符串;如果字符串没有匹配,则返回None; group返回匹配到的第一个,groups返回匹配到的全部
# 下面是个需要计算的表达式
(12+(34*6+2-5*(2-1)+(8+9-8*4)) # 都知道要先计算最里面的括号,但是这个表达式里最里面有2个括号,先计算哪一个?
# 这时候就能用到search()的好处了,它只取第一个,然后计算出结果替换括号,如此
# 反复匹配,计算,替换,直到所有的括号都处理完成。
下面是re模块里还没介绍到的方法:
- re.match():同search,不过仅仅在字符串开始处进行匹配。
- match匹配成功了跟search一样,返回一个对象;
- match没匹配成功,什么都不返回。
print(re.match("\d+", "alex36wuser33xialv33")) # 什么都没返回,在命令行里演示最直观
# 解释:什么都没返回,因为,match仅仅匹配开头,而开头是a,所以啥都匹配不到 # 所以,match()方法完全可以被search()方法取代
- re.split():对字符串进行分割
# 按空格分割
print(re.split(" ","hello abc def")) # ['hello', 'abc', 'def'] # 空格要分开,有管道符|的也要分开
print(re.split("[ |]","hello abc|def")) # ['hello', 'abc', 'def'] print(re.split("[ab]", "abc")) # ['', '', 'c']
# 为什么会有空出现?看下面的例子
# 解释:
# 首先会按照 "a" 分,abc里a的左变没有内容,就是一个空,a的右边是bc, 这样按照'a'分,就分成了两部分['','bc']
# 但是事还没完,因为是分割法是以a 或者 b分的,此时,再按b分,会把已经得到两部分['','bc']按照b再分,
# 其中,空的一部分肯定没有b了,就分不了了,而bc中有b,可以分,其中b的左边没内容,就是'',右边只有'c'
# 所以最后的结果是 ['', '', 'c'] # 按照a 或者 b 分
print(re.split("[ab]", "asdabcd")) # ['', 'sd', '', 'cd']
# 分割步骤:
# 1. 按a分,得到['','sdabcd']
# 2. 第二部分还有a,再按a分一次,得到['','sd','bcd']
# 3. 再按b分,得到['','sd','','cd']
- re.sub(pattern, repl, string, count=0, flags=0):替换
- pattern:匹配的条件
- repl:需要替换成的内容
- string:被匹配字符串
- count:匹配次数
- flags:
# 需求,把字符串里所有的数字替换为A print(re.sub("\d", "A", "jsadfja432143jkjfadkn343413"))
# jsadfjaAAAAAAjkjfadknAAAAAA print(re.sub("\d+", "A", "jsadfja432143jkjfadkn343413"))
# jsadfjaAjkjfadknA print(re.sub("\d", "A", "jsadfja432143jkjfadkn343413",4)) # 4代表,匹配的次数,匹配次数够了,后面的就不再匹配和替换了
# jsadfjaAAAA43jkjfadkn343413
- re.subn():返回共匹配了多少次,返回结果是元组
print(re.subn("\d+", "A", "jsadfja432143jkjfadkn343413"))
# ('jsadfjaAjkjfadknA', 2) print(re.subn("\d", "A", "jsadfja432143jkjfadkn343413"))
# ('jsadfjaAAAAAAjkjfadknAAAAAA', 12)
- re.compile():compile()里只有一个参数,就是匹配规则
# compile:是编译的意思
a = re.compile("\d+") # 先把这个匹配规则编译好,供后面的匹配直接调用就可以了 print(a.findall("fajfdaj34124134fadjflkjadfj23"))
# ['34124134', '23'] print(a.findall("民132nihakl4543na你啊打发343"))
# ['132', '4543', '343'] # 所以,当匹配一次的时候,用compile()和不用compile()没有什么区别,但是如果需要多次匹配不同的字符串,
# 如果都不用compile(),那每次匹配都会先编译一次规则,效率就会降低,
# 而如果用compile()先把规则编译好,再之后的多次匹配里直接调用,那效率就高了
- re.finditer():与findall()方法一样,都是匹配全部字符串,区别在于:
- findall()匹配后返回的结果放在了列表里
- finditer()匹配后的结果放在了一个迭代器对象里,返回的是个地址
- finditer()与findall()相比有什么好处呢?
- 当处理的数据非常庞大的时候,findall()都存在列表里肯定会耗内存,而finditer()直接存在一个迭代器对象里,只有当需要用里面的数据的时候再一条一条的拿出来,节省内存
- 用next().group()方法可以一条条的取出数据
a = re.compile("\d+")
b = a.finditer("fjadjf12323nfnnjfal123")
print(b) # <callable_iterator object at 0x00D451D0> # 如何取出迭代器对象里的结果?
# 用next().group()方法 print(next(b).group()) #
print(next(b).group()) # # print(next(b)) # <_sre.SRE_Match object; span=(6, 11), match='12323'>
- 匹配规则里还有分组时的两种情况:
- 1. 默认展示分组内符合匹配结果的内容
- 2.分组内加?: 可以去除默认规则
# 匹配规则里包含分组时,默认匹配结果只展示分组内的匹配结果
# 原理:程序认为,既然你分组了,那你肯定是最想看到分组里的匹配结果的,所以展示分组内的匹配结果
print(re.findall("www\.(baidu|163).com", "www.baidu.com")) # ['baidu']
print(re.findall("www.(baidu|163).com", "www.baidu.com")) # ['baidu']
print(re.findall("www.(baidu|163).com", "www.163.com")) # ['163'] # 如果不想只展示分组内的匹配结果,那么需要再分组内加上?:,来去除由默认规则,就会展示全部符合匹配规则的结果
print(re.findall("www.(?:baidu|163).com", "www.baidu.com")) # ['www.baidu.com']
print(re.findall("www.(?:baidu|163).com", "www.163.com")) # ['www.163.com']
关于模块导入的一点补充:
需求:把上面目录下的test.py导入到bin.py里
import sbsb
# sbsb.py 是导入的文件,是一个相对路径,既然是相对路径,总是相对于谁存在的。那么他到底是相对谁而存在呢?
# 相对路径最终是相对于当前路径(也就是bin的路径F:\workspace\OldBoy\源码\课件与源码\python全栈3期-课件与源码\day23\bin.py 去掉bin.py
# 这个文件名后,就变成当前路径了)继续拼接
# F:\workspace\OldBoy\源码\课件与源码\python全栈3期-课件与源码\day23\sbsb.py '''
import 导入的原理:
1. import 干的第一件事是帮过你去找到文件,通过上面路径拼接的方法找到文件
('F:\workspace\OldBoy\源码\课件与源码\python全栈3期-课件与源码\day23\sbsb.py')
2. import 干的第二件事是把导入的文件test.py copy 出来执行一遍 ''' # 上面执行程序会报错
解决办法是:
from module import sbsb
# from 做的工作,是生成这样一个相对路径, module/sbsb.py
# 然后与当前路径拼接成文件的最终路径:
F:\workspace\OldBoy\源码\课件与源码\python全栈3期-课件与源码\day23\module\sbsb.py 这样执行就不会报上面 No module named 'sbsb' 这个错,但是却报了下面的错
因为在sbsb.py文件里,导入了cal 模块,见下图。
那报上面错误的根本原因是什么呢?
还记得上面说的吧,import导入首先会做的两件事:
'''
1. import 干的第一件事是帮过你去找到文件,通过上面路径拼接的方法找到文件
('F:\workspace\OldBoy\源码\课件与源码\python全栈3期-课件与源码\day23\module\sbsb.py')
2. import 干的第二件事是把导入的文件test.py copy 出来执行一遍 ''' 问题就出在做第二件事的时候,copy出sbsb.py文件在bin.py里执行的时候,sbsb.py首先是导入cal.py文件的,那也就是要先找cal.py的路径,最终拼成绝对路径。
记住了,相对路径前的绝对路径永远都是当前执行文件的绝对路径,甭管你套了多少次的导入,永远都是当前执行文件(bin)的路径。所以,最后拼的路径是:
'F:\workspace\OldBoy\源码\课件与源码\python全栈3期-课件与源码\day23\cal.py'
这个路径当然找不到了,那怎么解决呢?
在sbsb.py文件里,把导入语句改写成: from module import cal
这样虽然解决了在bin.py文件下运行不保错的问题,但是如果在sbsb.py下运行,又会报错,因为此时sbsb.py变成了当前文件,运行时路径就被拼接成了:'F:\workspace\OldBoy\源码\课件与源码\python全栈3期-课件与源码\day23\module\module\cal.py' ,所以会报错。
备注
这个补充主要是为了说明,导入模块的原理:
1. 怎么识别当前路径与相对路径
2. 当前路径与相对路径的拼接
第二十二篇 正在表达式 re模块的更多相关文章
- Python开发【第二十二篇】:Web框架之Django【进阶】
Python开发[第二十二篇]:Web框架之Django[进阶] 猛击这里:http://www.cnblogs.com/wupeiqi/articles/5246483.html 博客园 首页 ...
- Python之路【第二十二篇】CMDB项目
浅谈ITIL TIL即IT基础架构库(Information Technology Infrastructure Library, ITIL,信息技术基础架构库)由英国政府部门CCTA(Central ...
- Python之路(第二十二篇) 面向对象初级:概念、类属性
一.面向对象概念 1. "面向对象(OOP)"是什么? 简单点说,“面向对象”是一种编程范式,而编程范式是按照不同的编程特点总结出来的编程方式.俗话说,条条大路通罗马,也就说我们使 ...
- Python之路(第十二篇)程序解耦、模块介绍\导入\安装、包
一.程序解耦 解耦总的一句话来说,减少依赖,抽象业务和逻辑,让各个功能实现独立. 直观理解“解耦”,就是我可以替换某个模块,对原来系统的功能不造成影响.是两个东西原来互相影响,现在让他们独立发展:核心 ...
- 第二十二篇、IO多路复用 一
一.简介io多路复用 可以监听多个文件描述符(socket对象)(文件句柄),一旦文件句柄出现变化,就会感知到 Linux中的 select,poll,epoll(内核2.6以上) 都是IO多路复用的 ...
- 第二十二篇:在SOUI中使用代码向窗口中插入子窗口
使用SOUI开发客户端UI程序,通常也推荐使用XML代码来创建窗口,这样创建的窗口使用方便,当窗口大小改变时,内部的子窗口也更容易协同变化. 但是最近不断有网友咨询如何使用代码来创建SOUI子窗口,特 ...
- Python之路【第二十二篇】:Django之Model操作
Django之Model操作 一.字段 AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - bi ...
- flask第二十二篇——模板【4】过滤器
请关注微信公众号:自动化测试实战 先来教大家一个pycharm设置默认模板的方法.我们每次新建模板或者平时写代码打开以后可能都要重复写# coding: utf-8这些代码,其实我们可以设置好模板,让 ...
- 【Python之路】第二十二篇--Django【基础篇】
1 Django流程介绍 MTV模式 著名的MVC模式:所谓MVC就是把web应用分为模型(M),控制器(C),视图(V)三层:他们之间以一种插件似的,松耦合的方式连接在一起. 模型负责业 ...
随机推荐
- Java 的 FileFilter文件过滤,readline读行操作
package com.cjonline.foundation.evisa; import java.io.BufferedReader; import java.io.File; import ja ...
- 总结的MR中连接操作
1 reduce side join在map端加上标记, 在reduce容器保存,然后作笛卡尔积缺点: 有可能oom 2 map side join 2.1 利用内存和分布式缓存,也有oom风险 2 ...
- 使用补丁破解IntelliJ IDEA 2017收费版本(转)
1. 首先去官网http://www.jetbrains.com/idea/download/#section=windows下载Ultimate版(注意不是community版)下载并安装.一定要记 ...
- ACL常用命令及工作原理
What ACL 是一系列 IOS 命令,根据数据包报头中找到的信息来控制路由器应该转发还是应该丢弃数据包.ACL 是思科 IOS 软件中最常用的功能之一. 在配置后,ACL 将执行以下任务: 限制网 ...
- 触发器 :new和 :old的使用
:new --为一个引用最新的列值;:old --为一个引用以前的列值; 这两个变量只有在使用了关键字 "FOR EACH ROW"时才存在,且update语句两个都有,而inse ...
- 如何使用Xcode的Targets来管理开发和生产版本的构建
如何使用Xcode的Targets来管理开发和生产版本的构建 想象一下,你已经完成了应用程序的开发和测试,现在准备提交正式版本.问题是,一些web服务的url指向了测试服务器,同时API密钥被配置用于 ...
- wsgiref手写一个web服务端
''' 通过wsgiref写一个web服务端先讲讲wsgiref吧,基于网络通信其根本就是基于socket,所以wsgiref同样也是通过对socket进行封装,避免写过多的代码,将一系列的操作封装成 ...
- Webpack4 学习笔记六 多页面配置和devtool
webpack 多页配置 webpack可以配置单页应用, 也可以配置多页应用. 区别在于, 单页应用entry入口只有一个, 而多页应用入口有多个 webpack配置: const path = r ...
- P1247 取火柴游戏
题目描述 输入k及k个整数n1,n2,-,nk,表示有k堆火柴棒,第i堆火柴棒的根数为ni:接着便是你和计算机取火柴棒的对弈游戏.取的规则如下:每次可以从一堆中取走若干根火柴,也可以一堆全部取走,但不 ...
- linux 2.6升级Python2.7 ./configure 报错问题
升级2.7.3使用命令./configure --prefix=/usr/local/python2.7.3时,出现以下错误:checking build system type... x86_64- ...