Python正则学习笔记


#1

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]匹配不在方括号内的任意字符。


#2

python的re模块应该也属于老NFA,除此之外还要重视大回溯问题。


#3

找到个网站可以检查一下写出来的正则表达式有没有毛病 https://regex101.com/%E6%90%9C%E7%8B%97%E6%88%AA%E5%9B%BE18%E5%B9%B409%E6%9C%8813%E6%97%A50359_7


Partners Wiki IRC