Python正则表达式
上次攻防赛在写自动提交flag脚本的时候,因为不会正则,在处理非标准flag格式的时候很捉急,赛后学了一下,并不是很难。。。只是临场写。。比较捉急。。所以很粗暴的用了分割字符串来处理flag。。
创建正则表达式对象
首先引入模块
import re
向re.compile()
传入一个字符串值,表示正则表达式,它将返回一个 Regex对象
如:
numRegex=re.compile(r'\d\d\d')
\d
表示0-9的数字,此时这个表达式匹配的就是连续的三个数字
过在字符串的第一个引号之 前加上 r,可以将该字符串标记为原始字符串
匹配Regex对象
Regex 对象的search()
方法将会查找传入的字符串,寻找该正则表达式的所有匹配。
如匹配字符串asd123fdsxxx
中的连续的三个字母
>>> import re
>>> numRegex=re.compile(r'\d\d\d')
>>> num=numRegex.search('asd123fds456xxx')
>>> print num.group()
123
会发现返回了第一个匹配到的连续三个数字123
如果想要找到所有匹配的地方,可以使用findall()
方法
>>> import re
>>> numRegex=re.compile(r'\d\d\d')
>>> num=numRegex.findall('asd123fds456xxx')
>>> print num
['123', '456']
此时会把所有匹配到的结果以一个list的形式返回出来
使用括号分组
有这样一串字符串china +86-1777
可以看成是电话号,前面的+86是国际区号
如果想将区号从电话号中分离,可以使用括号在正则表达式中创建分组
>>> numRegex=re.compile(r'\+(\d\d)-(\d\d\d\d)')
>>> str='china +86-1777'
>>> num=numRegex.search(str)
>>> print num.group(1)
86
>>> print num.group(2)
1777
>>> print num.group(0)
+86-1777
>>> print num.group()
+86-1777
用管道匹配多个分组
如字符串apple and pair
如果想匹配apple
或者pair
可使用正则表达式 r'apple|pair'
如果两个内容都出现在被查找的字符串中,第一次出现的匹配文本, 将作为 Match 对象返回
>>> regex=re.compile(r'apple|pair')
>>> mo1=regex.search('apple and pair')
>>> mo1.group()
'apple'
>>> mo2=regex.search('pair and apple')
>>> mo2.group()
'pair'
用?实现可选匹配
可选匹配的意思是不论这段文本在不在,正则表达式都会认为匹配
比如想匹配 hackernews
或者hackersnews
表达式的内容则为r'hacker(s)?news'
?可以理解为,“匹配这个问号之前的分组零次或一次”
>>> regex=re.compile(r'hacker(s)?news')
>>> mo1=regex.search('a hackernews hello')
>>> print mo1.group()
hackernews
>>> mo2=regex.search('a hackersnews hello')
>>> print mo2.group()
hackersnews
多次匹配
*
:匹配零次或多次
+
:匹配一次或多次
{x}
:匹配特定x次
{a,b}
:匹配[a,b]次,可以不限制最大值或者不限制最小值
>>> str1='test sr'
>>> str2='test sttr'
>>> str3='test stttr'
>>> regex=re.compile(r'st*r')
>>> regex2=re.compile(r'st+r')
>>> regex3=re.compile(r'st{3}r')
>>> mo1=regex.search(str1)
>>> mo2=regex2.search(str2)
>>> mo3=regex3.search(str3)
>>> print mo1.group()
sr
>>> print mo2.group()
sttr
>>> print mo3.group()
stttr
贪心匹配
Python 的正则表达式默认是“贪心”的,这表示在有二义的情况下,它们会尽可能匹配最长的字符串
比如 使用表达式r'(ha){3,5}'
去匹配’hahahahaha’
默认会匹配最长的字符串’hahahahaha’
如果要使用非贪心模式,则要在花括号后面加一个?
如r'(ha){3,5}?
此时会匹配最短的字符串’hahaha’
上面提到的其他多次匹配的方式同理
如r'(ha)+?'
,r'(ha)*?'
问号在正则表达式中可能有两种含义:声明非贪心匹配或表示可选的分组。这两种含义是完全无关的。
findall()方法
search()
将返回一个Match 对象,包含被查找字符串中的“第一次”匹配的文本,而findall()
方法将返回一组字符串,包含被查找字符串中的所有匹配。
如上面提到的:
如果想要找到所有匹配的地方,可以使用findall()
方法
>>> import re
>>> numRegex=re.compile(r'\d\d\d')
>>> num=numRegex.findall('asd123fds456xxx')
>>> print num
['123', '456']
常用字符
分类 | 含义 |
---|---|
\d | 0-9的数字 |
\D | 0-9以外的任意字符 |
\w | 任何字母,数字或下划线 |
\W | 除字母,数字,和下划线以外的字符 |
\s | 空格,制表符或换行符 |
\S | 除空格,制表符和换行符以外的字符 |
. | 通配符,匹配除了换行之外的所有 字符 |
创建字符分类
可以使用方括号定义自己的字符分类
如[aeiouAEIOU]
将匹配所有的元音字母
>>> vowelRegex = re.compile(r'[aeiouAEIOU]')
>>> vowelRegex.findall('RoboCop eats baby food. BABY FOOD.') ['o', 'o', 'o', 'e', 'a', 'a', 'o', 'o', 'A', 'O', 'O']
在字符分类的左方括号后加上一个插入字符(^),就可以得到“非字符类”。
将匹配不在这个字符类中的所有字符
>>> consonantRegex = re.compile(r'[^aeiouAEIOU]')
>>> consonantRegex.findall('RoboCop eats baby food. BABY FOOD.')
['R', 'b', 'c', 'p', ' ', 't', 's', ' ', 'b', 'b', 'y', ' ', 'f', 'd', '.', ' ', 'B', 'B', 'Y', ' ', 'F', 'D', '.']
完整匹配
在正则表达式的开始处使用插入符号(^),表明匹配必须发生在被查找文 本开始处。
可以再正则表达式的末尾加上美元符号($),表示该字符串必 须以这个正则表达式的模式结束。
可以同时使用^和$,表明整个字符串必须匹配该 模式,也就是说,只匹配该字符串的某个子集是不够的。 此时正则表达式将匹配整个字符串
如正则表达式 r’^\d+$'匹配从开始到结束都是数字的字符串
>>> wholeStringIsNum = re.compile(r'^\d+$')
>>> wholeStringIsNum.search('1234567890')
<_sre.SRE_Match object; span=(0, 10), match='1234567890'>
>>> wholeStringIsNum.search('12345xyz67890') == None True
>>> wholeStringIsNum.search('12 34567890') == None True
如果使用了^和$,那么整个字符串必须匹配该正则表达式。
不区分大小写
默认的正则表达式会匹配大小写,如果想不匹配大小写,可以向re.compile()
传入re.I
作为第二个参数
如:
>>> robocop = re.compile(r'rob', re.I)
速记
- ?匹配零次或一次前面的分组。
- *匹配零次或多次前面的分组。
- +匹配一次或多次前面的分组。
- {n}匹配 n 次前面的分组。
- {n,}匹配 n 次或更多前面的分组。
- {,m}匹配零次到 m 次前面的分组。
- {n,m}匹配至少 n 次、至多 m 次前面的分组。
- {n,m}?或*?或+?对前面的分组进行非贪心匹配。
- ^spam 意味着字符串必须以 spam 开始。
- spam$意味着字符串必须以 spam 结束。
- .匹配所有字符,换行符除外。
- \d、\w 和\s 分别匹配数字、单词和空格。
- \D、\W 和\S 分别匹配出数字、单词和空格外的所有字符。
- [abc]匹配方括号内的任意字符(诸如 a、b 或 c)。
- [^abc]匹配不在方括号内的任意字符。