Bootstrap

regex 正则表达式

regex 正则表达式

前言

**动机 : **

  1. 处理文本成为计算机主要工作之一
  2. 根据文本内容进行固定搜索是文本处理的常见工作
  3. 为了快速方便的处理上述问题,正则表达式技术诞生,逐渐发展为一个单独技术被众多语言使用

**定义 : **

即高级文本匹配模式,提供了搜索,替代等功能,本质是由一些字符和特殊符号组成的字串。
这个字串描述了字符和字符的重复行为,可以匹配某一类特征的字符串集合。

正则特点:

  • 方便进行检索和修改
  • 支持语言众多
  • 使用灵活变化多样
  • 文本处理,mongo存储某一类型字串,django、tornado路由,爬虫文本匹配

简单示例:

import re           # 标准模块

re.findall(regex,string)
功能 : 
    使用正则表达式匹配字符串
参数:
    regex   正则表达式
    string  目标字符串
返回值 : 匹配到的内容

元字符

即正则表达式中有特殊含义的字符,列表如下:

abc 等普通字符

元字符 : abc
匹配规则 : 匹配相应的普通字符
e.g. ab ----》 abcdef : ab

示例:

In [3]: re.findall('ab','abcdefabcde')
Out[3]: ['ab', 'ab']

| 符号两侧的正则均能匹配

e.g. ab|cd —》 abcdefgh : ab cd

示例:

re.findall('ab|fg','abcdefgabcde')          # 写成 re.findall('ab | fg', 'xxxx') 就完全不一样了,含空格
# ['ab', 'fg', 'ab']

补充:
错误的使用 “ab | cd” 将空格也包含进去了。匹配结果为空;

. 匹配任意一个字符 '\n’除外

e.g. f.o —》 foo fuo fao f@o

In [7]: re.findall('f.o','affooasand f@o,')
Out[7]: ['ffo', 'f@o']          # 注意,‘foo'就没有匹配出来,这是函数 findall() 规定的;

^ 匹配字符串开头

匹配规则: 匹配一个字符串的开头位置
e.g. ^Hello —> Hello world : Hello

In [13]: re.findall('^from', 'from the city.')
Out[13]: ['from']
In [31]: re.findall('^from', 'I come from the city.')       # from 非字符串首,匹配到空;
Out[31]: []

$ 匹配字符串结尾

匹配规则: 匹配一个字符串的结尾位置
e.g. py$ ----> hello.py : py

In [10]: re.findall('py$','hello.py')
Out[10]: ['py']
In [16]: re.findall('py$', 'python')            # 未匹配到,因为不是 'py' 结尾;
Out[16]: []

* 匹配重复0次或多次

元字符: * (常用)
匹配规则:匹配前面出现的正则表达式 0次或者多次
e.g. ab* 匹配这些 a ab abbbb…

In [26]: re.findall('.*py$', 'hello.py')
Out[26]: ['hello.py']

+ 匹配重复1次或多次

匹配规则: 匹配前面正则表达式至少一次

e.g ab+ ab abbbbb

In [29]: re.findall('.+\.py$','a.py')           # ’\.‘ 为转义;匹配*.py 文件名
Out[29]: ['a.py']

? 匹配重复0次或1次

匹配规则 : 匹配前面出现的正则表达式0次或1次
e.g. ab? a ab

In [34]: re.findall('ab?','abcdeabasdfabbbbbb')
Out[34]: ['ab', 'ab', 'a', 'ab']			# 贪婪模式(默认):明明可以只匹配'a',但是结果是'ab';

{N} 匹配重复指定次数

元字符 : {N}
匹配规则 : 匹配前面的正则表达式N次

e.g.   ab{3}  abbb

{M,N} 匹配重复指定次数范围

元字符 : {M,N}
匹配规则 : 匹配前面的正则表达式 m次到n次,范围 [m, n] 都是闭区间;

e.g ab{3,5} abbb abbbb abbbbb

In [39]: re.findall('ab{2,5}','abbcdeabbbabsdfabbbbbb')
Out[39]: ['abb', 'abbb', 'abbbbb']

[abcd] 字符集匹配

元字符 : [abcd]
匹配规则 : 匹配中括号中的字符集,或者是字符集区间 的一个字符

[abcd] ----》 a   b   c  d 
[0-9]  --->  1 3 4 7  匹配任意一个数字字符
[A-Z]  ---》 A D H    匹配任意一个大写字符
[a-z]  ---》 a d f h  匹配任意一个小写字符
[+-*/0-9a-g]  --》 + - *  /  4 b

注:多个字符集形式可以写在一起

In [42]: re.findall('^[A-Z][0-9a-z]{5}','Hello1 Join')
Out[42]: ['Hello1']
In [42]: re.findall('^[A-Z][0-9a-z]+','Hello1daxqd Join')
Out[42]: ['Hello1daxqd']

[^ …] 字符集不匹配

元字符 : [^ …]
匹配规则 : 匹配出字符集中字符的任意一个字符

e.g.

[^abcd]  ->    e   f  & #  
[^0-9] ->  a d g

In [48]: re.findall('[^_0-9a-zA-Z]','[email protected]')         # 匹配任意特殊符号;
Out[48]: ['@', '.']

\d 匹配任意数字字符

\D 匹配任意非数字字符

元字符 : \d 等价于 [0-9] , \D 等价于 [^0-9]
匹配规则: \d 匹配任意一个数字字符 \D 匹配任意非数字字符

In [49]: re.findall('1\d{10}','15100317766')
Out[49]: ['15100317766']

\w 匹配任意普通字符

\W 匹配任意特殊字符

元字符 : \w [_0-9a-zA-Z] \W [^_0-9a-zA-Z]
匹配规则 : \w 匹配数字字母下划线 \W 除了(数字字母下划线)

In [20]: re.findall('[A-Z]\w*', 'Hello World')
Out[20]: ['Hello', 'World']
In [21]: re.findall('\w+-\d+', 'wangming-26')           # 格式,'姓名-年龄'
Out[21]: ['wangming-26']

\s 匹配任意空字符

\S 匹配任意 非空字符

元字符 : \s \S
匹配规则: \s 任意空字符 [ \n\0\t\r] 空格 换行 回车 制表
\S 任意非空字符

In [61]: re.findall('hello\s+\S+','hello l&#l  hello   lucy  hellokadfh')
Out[61]: ['hello l&#l', 'hello   lucy']

^ 匹配字符串开头位置

$ 匹配字符串的结尾位置

\A 相当于’^’

\Z 相当于’$’

  • 匹配字符串开头结尾
    元字符 : \A ^ \Z $
    匹配规则: \A 表示匹配字符串开头位置 #相当于 ”^”
    \Z 表示匹配字符串的结尾位置 #相当于 “¥“
e.g.   \Aabc\Z    --->  abc             # 严格匹配 'abc'

\b 匹配单词边界

\B 匹配非单词边界

元字符: \b \B
匹配规则 \b 匹配一个单词的边界
\B 匹配一个单词的非边界

数字字母下划线和其他字符的交界处认为是 【单词边界】

is “This is a test”

In [71]: re.findall(r'\bis\b','This is a test')     # 注意 r'\bis\b' 中的 r ;
Out[71]: ['is']

元字符总结

字符: 匹配实际字符
匹配单个字符 :   .   \d  \D \w \W  \s  \S   [...]  [^...]
匹配重复次数 :  *   +   ?  {N} {M,N}
匹配字串位置 :  ^   $   \A   \Z   \b  \B
其他 :   |  

r"字串" 和 转义

转义

需要转义的字符: . * ? $ “” ‘’ [] () {} \

r —> 将字符串变为 raw 原始字串,不进行字符串的转义;(重要)

两种等价的写法

In [87]: re.findall(r'\? \* \\','what? * \\')
Out[87]: ['? * \\']

# 这是最正规的写法,但是太麻烦了,一般选择传 原始字符串给第一个参数;
In [88]: re.findall('\\? \\* \\\\','what? * \\')        
Out[88]: ['? * \\']

注意:
1. findall() 函数的两个参数都是字符串,第一个参数字符串用于生成 正则表达式 字符串,
第二个参数,用于生成待匹配的字符串;
2. 至于为什么前面可以直接使用 re.findall(‘hello\s+\S+\d\w+’,‘xxxx’),是因为没有\s等的转义字符,python 自动作了处理,
生成相应的正则表达式;
3. 若想偷懒,可以在两字符串参数前都加 ‘r’ ,这样就简单多了。

贪婪和非贪婪

和重复元字符相关

    • ? {m,n}

贪婪模式

在使用重复元字符的时候(* + ? {m,n}),元字符的匹配总是尽可能多的向后匹配更多内容,即为贪婪模式。
贪婪模式是一种默认情况

e.g. 尽可能多的匹配b

In [2]: re.findall('ab*', 'abbbbbalkjsfab')
Out[2]: ['abbbbb', 'a', 'ab']
In [90]: re.findall('ab+','abbbbbbalksdjfab')
Out[90]: ['abbbbbb', 'ab']

非贪婪模式

尽可能少的匹配内容,只要满足正则条件即可

贪婪 —》 非贪婪 *? +? ?? {m,n}? # “你这么贪,好吗?”, 多问了一下,想了一下,就不贪了。(可以这么理解)

尽量少匹配

In [95]: re.findall('ab*?', 'abbbbbbalksdjfab')
Out[95]: ['a', 'a', 'a']

In [96]: re.findall('ab+?','abbbbbbalksdjfab')
Out[96]: ['ab', 'ab']

正则表达式的分组

使用()为正则表达式分组 # 同C编程的正则,() 表示子匹配串;

注:
关于自组,就不用 re.findall() 函数演示了,因为它只显示子组的内容;
接下来使用 re.match()来演示;

((ab)cd(ef)) : 表示给ab分了一个子组

  1. 正则表达式的子组用()表示,增加子组后对整体的匹配没有影响

  2. 每个正则表达式可以有多个子组,子组由外到内、由左到右为第一第二第三。。。。。。子组
    ((ab)cd(ef))
    第一子组:(abcdef)
    第二子组:(ab)
    第三子组:(ef)

  3. 子组表示一个内部整体,很多函数可以单独提取子组的值
    In [108]: re.match(’(ab)cdef’,‘abcdefghig’).group(1) # group(1) 单独提取子组内容;
    Out[108]: ‘ab’
    In [30]: re.match(’(ab)cdef’,‘abcdefghig’).group(0) # 参数只能去 0~1,取2就报异常;
    Out[30]: ‘abcdef’

  4. 子组可以改变 重复行为,将子组作为一个整体重复

In [111]: re.match('(ab)*','ababababab').group()
Out[111]: 'ababababab'

捕获组和非捕获组 (命名组和非命名组)

格式 : (?Pregex)
示例:
(ab)cdef --》 (?Pab)cdef

作用:

  1. 某些函数可以通过名字提取子组内容,或者通过名字进行键值对的生成。
In [112]: re.match('(?P<word>ab)cdef','abcdefghi').group() 
Out[112]: 'abcdef'
  1. 起了名字的子组可以通过名称重复使用
    (?P=name)
    示例:
    (ab)cdef(ab) --》 (?Pab)cdef(?P=word)
In [114]: re.match('(?P<word>ab)cd(?P=word)','abcdabdfewf').group()
Out[114]: 'abcdab'

练习 :

匹配长度为8-10位的密码。必须以字母开头,数字字母下划线组成
1\w{7,9}$

匹配身份证号
\d{17}(\d|x) # 最后一位是数字或x

re模块

compile(pattern, flags=0)
功能 : 获取正则表达式对象
参数 : pattern 正则表达式
flags 功能标志位 提供正则表达式结果的辅助功能
返回值:返回相应的正则对象

In [15]: obj = re.compile('abc')
In [16]: help(obj.findall)      # 可以比较 re 模块的 findall() 方法;
In [17]: dir(obj)                   # 查看对象属性和方法;

compile对象属性:

groupindex : compile对象属性 得到捕获组名和第几组数字组成的字典

groups: compile 对象属性,得到一共有多少子组

示例见 regex.py

  • compile 函数返回对象的属性函数 和 re模块属性函数有相同的部分

相同点:
*功能完全相同

不同点:
compile返回值对象属性函数参数中没有pattern和flags部分,因为这两个参数内容在compile生成对象时已经指明,
而re模块直接调用这些函数时则需要传入

compile返回值对象属性函数参数中有pos 和 endpos参数,可以指明匹配目标字符串的起始位置,
而re模块直接调用这些函数时是没有这个

findall(string,pos=0, endpos=9223372036854775807) #compile对象 方法
功能 : 将正则表达式匹配到的内容存入一个列表返回
参数 : 要匹配的目标字符串
返回值: 返回匹配到的内容列表

  • 如果正则表达式中有子组,则返回子组的匹配内容,同样也是一个列表;
    示例:
    In [18]: pattern = r’(ab)cd(ef)’
    In [19]: obj = re.compile(pattern)
    In [20]: L = obj.findall(‘abcdefghig’)
    In [21]: L
    Out[21]: [(‘ab’, ‘ef’)]
    In [22]: obj.match(‘abcdefghig’).group() # 显示全部
    Out[22]: ‘abcdef’
    In [23]: obj.match(‘abcdefghig’).group(1) # 第一子组 group(1)
    Out[23]: ‘ab’
    In [24]: obj.match(‘abcdefghig’).group(2) # 第二子组 group(2)
    Out[24]: ‘ef’

finditer(string, pos=0, endpos=9223372036854775807)
功能 : 同findall 查找所有正则匹配到的内容
参数:同findall
返回值 : 返回一个迭代器,迭代的每一项都是一个match 对象
示例:

    In [38]: obj = re.compile(r'foo')
    In [39]: itt = obj.finditer('foo, food on the table.')
    In [40]: for i in itt:
        ...:     print(i.group())
    foo
    foo

split(pattern, string, maxsplit=0, flags=0)
split(string, maxsplit=0)
功能: 以正则表达式切割字符串
返回值 : 分割后的内容放入列表
示例:
In [25]: re.split(r’\s+’, ‘hello world nihao china’)
Out[25]: [‘hello’, ‘world’, ‘nihao’, ‘china’]

sub(repl, string, count=0)
sub(pattern,re_string,string,count=0, flags=0) # 源字符串不变,返回一个新的字符串;
功能:用目标字符串替换正则表达式匹配内容
参数:re_string 用什么来替换
string 要匹配的目标字符串
count 最多替换几处
返回值 : 返回替换后的字符串
示例:
In [28]: re.sub(r’[A-Z]’, ‘##’, ‘Hi, Jame. It is a sunny day.’, 2)
Out[28]: ‘##i, ##ame. It is a sunny day.’

subn(pattern, repl, string, count=0, flags=0)
subn(repl, string, count=0)
功能 : 同sub
参数 : 同sub
返回值 : 比sub多一个实际替换的个数
示例:
In [30]: re.subn(r’[A-Z]’, ‘##’, ‘Hi, Jame. It is a sunny day.’, 2)
Out[30]: (’##i, ##ame. It is a sunny day.’, 2)
In [31]: re.subn(r’[A-Z]’, ‘##’, ‘Hi, Jame. It is a sunny day.’)
Out[31]: (’##i, ##ame. ##t is a sunny day.’, 3) # 实际替换3处;

match(pattern, string, flags=0)
match(string, pos=0, endpos=9223372036854775807)
功能:匹配一个字符串开头的位置
参数:
pattern 正则表达式
string 目标字符串 (字符串开头)
返回值 : 如果匹配到返回 一个match obj
没匹配到返回None,抛出异常 AttributeError.

search(pattern, string, flags=0)
search(string, pos=0, endpos=9223372036854775807)
功能:同match 只是可以匹配任意位置,只能匹配一处
参数:目标字符串
返回值 : 如果匹配到返回 一个match obj
没匹配到返回None

fullmatch(pattern, string, flags=0)
fullmatch(string, pos=0, endpos=9223372036854775807)
要求目标字符串能够被正则表达式完全匹配

In [12]: obj = re.fullmatch(’\w+’,‘abcd2’)
In [13]: obj.group()
Out[13]: ‘abcd2’

=================================================================
match 对象属性及函数

‘end’
‘endpos’
‘group’
‘groupdict’
‘groups’
‘lastgroup’
‘lastindex’
‘pos’
‘re’,
‘span’
‘start’

group(n=0)
功能:获取match 对象匹配到的内容
参数:默认为0,表示获取整体匹配

groupdict()

flags: re 直接调用的匹配函数大多有 flags 参数,功能为辅助正则匹配的标志位;

I, IGNORECASE 匹配时,忽略大小写;
S, DOTALL 匹配换行,对 . 元字符起作用;
M, MULTILINE 开头结尾计算换行,对^$起作用;(对\A \Z 不起作用)
X, VERBOSE 让正则能以#添加注释,还必须换行;(基本不用)

同时添加多个flags
如 re.S | re.I


  1. a-zA-Z ↩︎

;