python 中的正则表达式

python 中的正则表达式

python 是通过 re 模块(Regular Expression)提供的各种功能,是 python 内嵌的模块。完整的正则表达式涉及的内容很多,以下只是记录了一些比较常用的使用方法。

常用函数

re.Match

先介绍一下 re.Match 类,其内容包含了两个部分:‘span’ 匹配到的字符串在原串中的头尾索引,‘match’ 是匹配到的最终字符串。

Match.group()

返回匹配到的字符串的结果,没有匹配到则是 None。

Match.start()

返回匹配的字符串首位在原串中的索引位置。

Match.end()

返回匹配的字符串末位在原串中的索引位置。

>>> re.match('[a-z]*', 'hello world').group()
'hello'
>>> re.match('[a-z]*', 'hello world').start()
0
>>> re.match('[a-z]*', 'hello world').end()
5

匹配

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

判断一个正则表达式是否从字符串的开头处匹配,如果不满足条件则返回 None, 返回 re.Match。

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

遍历字符串,找到第一个满足正则表达式返回 re.Match,若无满足的返回 None。

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

遍历字符串,找出所有正则表达式的子串并以列表形式返回,若无则返回一个空列表。

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

遍历字符串,找出所有正则表达式的子串并以迭代器形式返回。

>>> print re.match('[a-z]*', 'hello world').group()
hello
>>> print re.search('[a-z]*', 'hello world').group()
hello
>>> print re.findall('[a-z]*', 'hello world')
['hello', '', 'world', '']
>>> print re.finditer('[a-z]*', 'hello world')
<callable-iterator object at 0x0394F350>

替换

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

先找出所有匹配的字符串,用 repl 来代替匹配的字符串,count 为替换的次数,默认为0,如无匹配则无改变的返回。

>>> re.sub('[0-9]+', ' ', 'ni38shi3434342yi313ge32415ren!',3)
'ni shi yi ge32415ren!'

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

功能同上,但是会返回替换后的字符串以及替换次数的两元组,如下:

>>> re.subn('[0-9]+', ' ', 'ni38shi3434342yi313ge32415ren!',6)
('ni shi yi ge ren!', 4)

分割

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

先匹配出所有字符串,用匹配出的字符串当做分隔符,然后将分割结果以 list 的形式返回。

>>> re.split('a', 'dsfadasgaaeaf',10)
['dsf', 'd', 'sg', '', 'e', 'f']

特殊的函数

re.compile(pattern)

将正则表达式语句编译成新的对象,这样如果需要重复用到一个规则是就很方便了,同时也能够提高效率。

>>> e=re.compile('\w+')
>>> s=e.findall('wo shi zhong guo ren!')
>>> print s
['wo', 'shi', 'zhong', 'guo', 'ren']

re.escape(pattern)

将字符串中所有非字母数字更换为转义字符并返回。

>>> re.escape('12\dae#df\ns      t')
'12\\\\dae\\#df\\\ns\\\t\\ t'

 

正则表达式语法

正则表达式的规则其实用字符串去寻找相同的字符串,如 test 去匹配 math test ,数字也一样。如果仅仅只是这样的话,倒也没必要继续介绍下去,因为有一类特殊的字符的存在,称之为元字符 。

. ^ $ * + ? { } [ ] \ | ( )

[ ]

中括号,用来指定字符类,因为字母和数字都是连续的,所以可以用 “-” 来指定一系列的字母或数字。如:[abcde] 和 [a-e] 是等价的。但是 - 在首位或则末位是匹配其本身如:[-abc] 匹配的是 ‘-’,‘a’, 'b', 'c'。

括号中的元字符是没有特殊意义的,会匹配其本身。

当括号中首位字符为 ^ 时表示取反,表示除了括号中的这些字符外的所有字符。如果在其他位置,只表示其本身。

当需要匹配 [ ] 本身时,有两种方式,一种是加上 \ 转义,另一种就是把 ] 放在首位,破坏配对。

>>> re.findall('[^a]', 'dsfkhab')
['d', 's', 'f', 'k', 'h', 'b']
>>> re.findall('[-abc]', 'a-bcdag')
['a', '-', 'b', 'c', 'a']
>>> re.findall('[a^]', 'adf^dfadsf*')
['a', '^', 'a']
>>> re.findall('[][]', '[df]3\77\[\d\f]adsf*')
['[', ']', '[', ']']
>>> re.findall('[[\]]', '[df]3\77\[\d\f]adsf*')
['[', ']', '[', ']']

\

转义符,需要使用任何的特殊字符,在前面加上 \ 即可,此外 \ 加上字母表示一些特殊的集合。

\d
匹配任何十进制数字;这等价于类 [0-9]。
\D
匹配任何非数字字符;这等价于类 [^0-9]。
\s
匹配任何空白字符;这等价于类 [ \t\n\r\f\v]。
\S
匹配任何非空白字符;这相当于类 [^ \t\n\r\f\v]。
\w
匹配任何字母与数字字符;这相当于类 [a-zA-Z0-9_]。
\W
匹配任何非字母与数字字符;这相当于类 [^a-zA-Z0-9_]。
\b
匹配单词边界,返回的匹配结果里是不包含空白,而 \s 是会包含的。
\B
匹配非单词边界
>>> re.findall( r'\Bbc\w+', 'abc abcd bc bce')
['bcd']

.

匹配任何除了换行符以外的所有字符,当指定 re.DOTALL 标记时,可匹配任意字符(包括换行符)。

^

匹配字符串的开头,与中括号里表示的意义不一样。MULTILINE (设置 re.M) 的情况下,会默认匹配换行符后面的第一个字符。\A 也只匹配开头,即使在 M 的模式下也只匹配字符串首位。

>>> re.findall('^3\w', '3ds3fk3h3acb')
['3d']
>>> re.findall('^3\w', '3ds3fk\n3h3acb',re.M)
['3d', '3h']

$

匹配字符串的末位,MULTILINE (设置 re.M) 的情况下,会默认匹配换行符前面的字符。\Z 只匹配字符串的结尾,即使在 M 的模式下也只匹配字符串末尾。

>>> re.findall('foo.$', 'foo1\nfoo2\n')
['foo2']
>>> re.findall('foo.$', 'foo1\nfoo2\n', re.M)
['foo1', 'foo2']

*

表示前面字符重复 0 次到任意次,如:‘ab*’ 可以匹配到 ‘a’,‘ab’, 'abb'……等好多个。这里的话一般情况下,会按照贪心算法来匹配,除非特殊设定后,后面再讲。

>>> re.findall('fo*', 'f1\nfoo2\n')
['f', 'foo']

+

表示前面字符重复 1 次到任意次,和 * 类似,只是要求至少出现一次。

>>> re.findall('fo+', 'f1\nfoo2\n')
['foo']

?

表示前面的字符最多出现 1次。

>>> re.findall('fo?', 'f1\nfoo2\n')
['f', 'fo']

*? +? ??

前面说了,正常情况下 * ,+,? 都是贪心算法,但是在其后面加上 ?后就不一样了,总是以最小匹配长度来匹配。

>>> re.findall('<.*>', '<a>b<c>')
['<a>b<c>']
>>> re.findall('<.*?>', '<a>b<c>')
['<a>', '<c>']

{ }

用来表示前面的字符应该匹配多少次,分为两种情况:'a{3}' 这是匹配连续 3 个 a,‘{3, 6}’ 匹配连续 3 到 6 个 a,‘{3,}’ 匹配至少 3 个 a。依然遵循贪心匹配规则,如果不希望这样的话和上面一样,在后面加上 ? 即可,如:

>>> re.findall('a{3,6}', 'aaaaaaaa')	# 8个a
['aaaaaa']
>>> re.findall('a{3,6}', 'aaaaaaaaa')	# 9个a
['aaaaaa', 'aaa']
>>> re.findall('a{3,6}?', 'aaaaaaaa')	# 8个a
['aaa', 'aaa']

|

或,A|B 匹配 A 或者 B 均可,会优先匹配 A,如满足就不再匹配 B。

>>> re.findall('ab|bc', 'abc')
['ab']

简单的理解()括起来的就是一个组。

捕获组

捕获组会根据最终匹配的结果将结果存储在内存中,可通过反引用的方式(\number 或 \name)来使用捕获组的匹配结果。

捕获组包含两种:

一种是正常的 (pattern),其结果会按数字编号来存储,0 代表整个表达式的匹配结果,1 代表第一个组的匹配结果,以此类推。

另一种叫命名捕获组 (?P<name>pattern),其结果会以 name 存储,通过反引用就可以来查看或使用其结果。

>>> s = "<html><h1>learning is good!</h1></html>"
>>> p = r"<(.+)><(.+)>(?p<content>.+)</\2></\1>"
>>> m = re.match(p, s)
>>> m.groups()
('html', 'h1', 'learning is good!')
>>> print m.group(1),m.group('content')
html learning is good!

非捕获组

看这名字不太好理解,非捕获组匹配到的结果并不会保存在内存,也就不存在反引用了。形式为:(?:pattern)

这里有个注意的点,就是 findall 使用时,捕获组和非捕获组的结果是不一样的。如:

>>> s='123@qq.com345@163.com126@126.com'
>>> re.findall(r'\w+@(qq|163|126).com', s)
['qq', '163', '126']
>>> re.findall(r'\w+@(?:qq|163|126).com', s)
['123@qq.com', '345@163.com', '126@126.com']

这个原因是 findall() ,如果存在一个或者多个组,那么结果将会返回一个组列表或则是多个组组成的元组列表。

零宽断言

用于查找特定内容之前或之后的内容,但是不包括特定内容本身。

  • (?=exp)

    匹配exp后面的位置

  • (?<=exp)

    匹配exp前面的位置

  • (?!exp)

    匹配后面跟的不是exp的位置

  • (?<!exp)

    匹配前面不是exp的位置

>>> s="regex represents regular expression"
>>> re.search(r're(?=sents)', s).span()
(9, 11)
>>> re.search(r're(?!sents)', s).span()
(0, 2)
>>> re.search(r'(?<=p)re', s).span()
(9, 11)
>>> re.search(r'(?<!p)re', s).span()
(0, 2)

注释

(?#) python 允许在表达式中写注释,匹配时会忽略其内容。

标志修饰符

简写全称描述
re.Ire.IGNORECASE使匹配对大小写不敏感
re.Lre.LOCALE做本地化识别(locale-aware)匹配,不建议使用
re.Mre.MULTILINE多行匹配,影响 ^ 和 $
re.Sre.DOTALL使 . 匹配包括换行在内的所有字符
re.Are.ASCII\w, \W, \b, \B, \d, \D, \s\S 只匹配ASCII,而不是Unicodere
re.Ure.UNICODE根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B
re.Xre.VERBOSE忽略空白,使能够更加自由的编写表达式,如添加注释
a = re.compile(r"""\d +  # the integral part
                   \.    # the decimal point
                   \d *  # some fractional digits""", re.X)
b = re.compile(r"\d+\.\d*")

这些修饰符怎么用呢,可以翻看上面的常用函数,里面有个 flags 的参数,就是用来传递这些修饰符的,当然可以用 | 来使多条同时生效,如:re.I | re.M 就是忽略大小写同时开启多行模式。

此外,还可以通过另外一种方式来规定使用什么模式,(?aiLmsux-imsx:pattern) ,这里的模式只对括号内的有效。其中前面的是设置一个或多个模式,而 - 后面的是去除哪些模式。

一些常用的正则表达式

校验数字的表达式

  1. 数字:^[0-9]*$

  2. n位的数字:^\d{n}$

  3. 至少n位的数字:^\d{n,}$

  4. m-n位的数字:^\d{m,n}$

  5. 零和非零开头的数字:^(0|[1-9][0-9]*)$ *

  6. 非零开头的最多带两位小数的数字:`^(1-9)+(.[0-9]{1,2})?$`

  7. 带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})?$

  8. 正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$

  9. 有两位小数的正实数:^[0-9]+(.[0-9]{2})?$

  10. 有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$

  11. 非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$*

  12. 非零的负整数:`^-1-90-9"$ 或 ^-[1-9]\d$`

  13. 非负整数:`^\d+$ 或 ^[1-9]\d|0$`

  14. 非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$*

  15. 非负浮点数:`^\d+(.\d+)?$ 或 ^[1-9]\d.\d|0.\d[1-9]\d|0?.0+|0$`

  16. 非正浮点数:`^((-\d+(.\d+)?)|(0+(.0+)?))$ 或 ^(-([1-9]\d.\d|0.\d[1-9]\d))|0?.0+|0$`

  17. 正浮点数:`^[1-9]\d.\d|0.\d[1-9]\d$ 或 ^(([0-9]+.[0-9]1-9)|([0-9]1-9.[0-9]+)|([0-9]1-9))$`

  18. 负浮点数:`^-([1-9]\d.\d|0.\d[1-9]\d)$ 或 ^(-(([0-9]+.[0-9]1-9)|([0-9]1-9.[0-9]+)|([0-9]1-9)))$`

  19. 浮点数:`^(-?\d+)(.\d+)?$ 或 ^-?([1-9]\d.\d|0.\d[1-9]\d*|0?.0+|0)$`

校验字符的表达式

  1. 汉字:^[\u4e00-\u9fa5]{0,}$

  2. 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$

  3. 长度为3-20的所有字符:^.{3,20}$

  4. 由26个英文字母组成的字符串:^[A-Za-z]+$

  5. 由26个大写英文字母组成的字符串:^[A-Z]+$

  6. 由26个小写英文字母组成的字符串:^[a-z]+$

  7. 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$

  8. 由数字、26个英文字母或者下划线组成的字符串:^\w+$^\w{3,20}$

  9. 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$

  10. 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$

  11. 可以输入含有^%&',;=?$"等字符:[^%&',;=?$\x22]+

  12. 禁止输入含有~的字符:[^~\x22]+

特殊需求表达式

  1. Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$

  2. 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?

  3. InternetURL:[a-zA-z]+://[^\s]*或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$

  4. 手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$

  5. 电话号码("XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$

  6. 国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}

  7. 身份证号(15位、18位数字):^\d{15}|\d{18}$

  8. 短身份证号码(数字、字母x结尾):^([0-9]){7,18}(x|X)?$^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$

  9. 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$

  10. 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$

  11. 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$

  12. 日期格式:^\d{4}-\d{1,2}-\d{1,2}

  13. 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$

  14. 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$

  15. 钱的输入格式:

    • 有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "分" 的 "10000" 和 "10,000":^[1-9][0-9]*$*

    • 这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:`^(0|1-9)$`

    • 一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$

    • 这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$

    • 必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10" 和 "10.2" 是通过的:^[0-9]+(.[0-9]{2})?$

    • 这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$

    • 这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$*

    • 1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:`^([0-9]+|[0-9]{1,3}(,[0-9]{3}))(.[0-9]{1,2})?$`

  16. 备注:这就是最终结果了,别忘了"+"可以用"*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里

  17. xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]

  18. 中文字符的正则表达式:[\u4e00-\u9fa5]

  19. 双字节字符:[^\x00-\xff](包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))

  20. 空白行的正则表达式:\n\s\r (可以用来删除空白行)

    • HTML标记的正则表达式:<(\S*?)[^>]*>.*?</\1>|<.*? />(网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)

    • 首尾空白字符的正则表达式:^\s*|\s*$(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)

  21. 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)

  22. 中国邮政编码:[1-9]\d{5}(?!\d)(中国邮政编码为6位数字)

  23. IP地址:\d+\.\d+\.\d+\.\d+ (提取IP地址时有用) 34 IP地址:((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值