re模块 Python的正则表达式模块为re,可以使用import re
导入,常用的正则表达式方法包括:
re.search(pattern, string, flags=0)
:在字符串string中搜索匹配正则表达式pattern的第一个位置,并返回相应的match对象。
re.match(pattern, string, flags=0)
:从字符串string的开头匹配正则表达式pattern,并返回相应的match对象。
re.findall(pattern, string, flags=0)
:返回字符串string中所有匹配正则表达式pattern的子串列表。
re.sub(pattern, repl, string, count=0, flags=0)
:将字符串string中所有匹配正则表达式pattern的地方替换成repl。
其中pattern为要匹配的正则表达式,可以包含字符集、特殊字符、模式修正符等。
原生字符串 在 Python 中,字符串前面加上字母 r
表示这是一个”原生字符串”。原始字符串中的特殊字符 (比如反斜杠\
)会被当成普通字符对待,而不是转义字符 。在正则表达式中,有很多特殊符号需要用反斜杠来进行转义,如果不使用原生字符串,需要使用双反斜杠\\
来表示一个反斜杠 。例如,如果我们要匹配一个字符串中的单词边界,在正则表达式中应该使用\b
,但是如果不使用原生字符串,在 Python 中需要写成"\\b"
;而如果使用原始字符串,只需要写成r'\b'
即可。
当字符串中包含反斜杠\
时,为了避免反斜杠被误解读或转义,我们需要使用原始字符串,下面是一个例子:
1 2 3 4 5 6 7 str1 = "C:\\Windows\\System32\\cmd.exe" print (str1) str2 = r"C:\Windows\System32\cmd.exe" print (str2)
可以看到,在不使用原始字符串的时候,每个反斜杠都需要用两个反斜杠来表示。而使用原始字符串的时候,就不需要进行转义了。同样的,在正则表达式中如果想要匹配字符\
,也需要使用原始字符串,例如:
1 2 3 4 5 6 7 import restr = "a\\b\\c" pattern = r"\\" result = re.findall(pattern, str ) print (result)
这里使用了原始字符串,表示正则表达式中要匹配的是一个反斜杠。如果不使用原始字符串,则需要写成"\\\\"
,表示要匹配两个反斜杠。
模式字符串 正则表达式能按照某种模式匹配一系列有相似特征的字符串 ,在正则表达式(Regex)里使用特殊的语法来表示正则表达式:
1. ^
匹配字符串的开头,$
匹配字符串的结尾:
1 2 3 4 5 import restr ='helloworld' d=re.match (r'^hello$' ,str ) print (d)
这里在匹配的时候同时使用了上面两个:^hello$
,^
表示字符串的开头,$
表示字符串的结尾。由于”helloworld”不是”hello”,因此re.match()
方法无法找到匹配的内容,所以输出为None
。
如果匹配为r'^hello'
,那么就可以匹配到字符串了。
2. *
匹配0次或者多次前面出现的正则表达式,+
匹配1次或者多次前面出现的正则表达式,?
匹配0次或者1次前面出现的正则表达式,下面是几个简单的小例子:
1 2 3 4 5 6 7 import restr = "aaabbabbabab" pattern = r"ab*" result = re.findall(pattern, str ) print (result)
1 2 3 4 5 6 7 import restr = "32 apple, 73 pears, 63 oranges" pattern = r"\d+" result = re.findall(pattern, str ) print (result)
1 2 3 4 5 6 7 8 9 10 import restr1 = "abc" str2 = "bcd" pattern = r"a?bc" result1 = re.findall(pattern, str1) result2 = re.findall(pattern, str2) print (result1) print (result2)
3. .
匹配任何字符,a|b
匹配a
或者b
:
1 2 3 4 5 6 7 8 9 10 11 12 13 import restr1 = "abc" str2 = "def" str3 = "e4e" pattern = r".+." result1 = re.findall(pattern, str1) result2 = re.findall(pattern, str2) result3 = re.findall(pattern, str3) print (result1) print (result2) print (result3)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import restr1 = "cat" str2 = "car" str3 = "bat" str4 = "bar" pattern = r"cat|bat" result1 = re.findall(pattern, str1) result2 = re.findall(pattern, str2) result3 = re.findall(pattern, str3) result4 = re.findall(pattern, str4) print (result1) print (result2) print (result3) print (result4)
4. []
匹配来自字符集的单一字符 ,[^]
不匹配这个字符集的任何一个字符:
1 2 3 4 5 6 7 8 9 10 11 12 13 import restr1 = "apple" str2 = "banana" str3 = "cat" pattern = r"[aeiou]+" result1 = re.findall(pattern, str1) result2 = re.findall(pattern, str2) result3 = re.findall(pattern, str3) print (result1) print (result2) print (result3)
1 2 3 4 5 6 7 8 9 10 11 12 13 import restr1 = "apple" str2 = "banana" str3 = "cat" pattern = r"[^aeiou]" result1 = re.findall(pattern, str1) result2 = re.findall(pattern, str2) result3 = re.findall(pattern, str3) print (result1) print (result2) print (result3)
5. {x}
匹配x次前面出现的正则表达式,{x,y}
匹配x到y次前面出现的正则表达式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import restr1 = "123" str2 = "1234" str3 = "12" str4 = "12345" pattern = r"^\d{3}$" result1 = re.findall(pattern, str1) result2 = re.findall(pattern, str2) result3 = re.findall(pattern, str3) result4 = re.findall(pattern, str4) print (result1) print (result2) print (result3) print (result4)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import restr1 = "123" str2 = "1234" str3 = "12" str4 = "12345" pattern = r"^\d{3,5}$" result1 = re.findall(pattern, str1) result2 = re.findall(pattern, str2) result3 = re.findall(pattern, str3) result4 = re.findall(pattern, str4) print (result1) print (result2) print (result3) print (result4)
6. \w
匹配数字字母下划线,\W
匹配非数字字母下划线,\s
匹配任意空白字符([\t\n\r\f]
),\S
匹配任意非空字符,\d
匹配任意数字([0-9]
),\D
匹配任意非数字。
组 正则表达式中的组可以分为两种,分别是捕获组和非捕获组。而捕获组中又包含了子组这一概念。
捕获组是通过 (...)
来表示的,它不仅可以用来对子表达式进行分组,还可以在正则表达式中捕获其匹配的内容。如果正则表达式匹配成功,捕获组中的内容可通过 MatchObject
对象的 group()
或 groups()
方法来获取。当我们使用括号将部分正则表达式括起来时,就形成了一个组,它可以被看做一个整体,可以被使用和调用。
例如,在匹配嵌套括号的表达式中,我们可以使用正则表达式中的子组来匹配表达式中的内部括号:
1 2 3 4 5 6 7 8 9 10 11 12 13 import retext = "Hello, my name is Alice. I am 25 years old." pattern = r"my name is (\w+)\. I am (\d+) years old\." match = re.search(pattern, text)if match : name = match .group(1 ) age = match .group(2 ) print ("Name: %s, Age: %s" % (name, age)) else : pass
引用组 对组进行引用,可以使用\1
等进行引用避免重复书写:
1 2 3 4 5 6 7 8 9 import retext = "ha-ha,haa-haa" pattern = r"(ha)-\1,(haa)-\2" match = re.search(pattern, text)print (match .group()) print (match .groups())
非捕获组 可以对表达式进行被引用捕获的分组,使用(?:)
:
1 2 3 4 5 6 7 8 9 import retext = "ha-ha,haa-haa" pattern = r"(?:ha)-ha,(haa)-\1" match = re.search(pattern, text)print (match .group()) print (match .groups())
贪婪模式 正则表达式中的量词默认是贪婪模式(greedy),也就是尽可能匹配多的字符 。例如,正则表达式 \d{1,3}
可以匹配 1 到 3 个数字,它会优先尝试匹配 3 个数字。但是,如果一个量词后面跟着一个问号 ?
,则变成了非贪婪模式 (non-greedy,或称懒惰模式),它会尽可能匹配少的字符:
1 2 3 4 5 6 7 8 9 10 11 12 13 import retext = "aaaab" pattern_greedy = "a*" match1 = re.match (pattern_greedy, text) print (match1.group())pattern_non_greedy = "a*?" match2 = re.match (pattern_non_greedy, text) print (match2.group())
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import retext = "Hello, my name is Alice. I am 25 years old." pattern = r"my name is (\w+)\. I am (\d+) years old\." match = re.search(pattern, text)if match : print (match .group()) print (match .groups()) name = match .group(1 ) age = match .group(2 ) print ("Name: %s, Age: %s" % (name, age)) else : pass
实例 在一道交互RSA题目中,得到返回值:
1 2 3 4 5 ... [+]N = 5620091920974256422410841191105205840767375685678362100885830811646534394732329834493466519601188307840783582915972737445895078412610949614439775710731847 [+]e = 65537 [+]input your option: ...
为了更方便的得到N和e的值,要用到正则表达式:
1 2 3 4 5 6 7 8 9 10 11 12 n_and_e = p.recvuntil(b'[+]input your option:' ).decode() n_match = re.search(r'N\s*=\s*(\d+)' , n_and_e) n = int (n_match.group(1 )) e_match = re.search(r'e\s*=\s*(\d+)' , n_and_e) e = int (e_match.group(1 )) print (f'n = {n} ' )print (f'e = {e} ' )
re.search()
函数可以通过正则表达式在给定的文本中搜索匹配项。这里的正则表达式用于匹配输出文本中 N =
和 e =
后面的数字,并提取出数字作为 RSA 公钥的模数和指数。
详细解释一下这个正则表达式的构成:
1 re.search(r'N\s*=\s*(\d+)' , n_and_e)
这个正则表达式共由三个不同的部分构成,每个部分的含义如下:
N
:表示要匹配的第一个字符,即 N 字符。
\s*=\s*
:表示匹配 N
后面可能出现一些空白符(用 \s*
来表示),然后再匹配“等于”号(即 =
),然后再可能出现一些空白符。
(\d+)
:表示捕获所有的数字字符(用 \d+
来表示),并将其保存到分组中。
所以这个正则表达式可以匹配类似如下的文本:
在这种情况下,第一个分组 (1234567890)
中保存了数字 1234567890(即 N 的值)。
同理,e\s*=\s*(\d+)
也是匹配类似如下的文本:
在这种情况下,第一个分组 (65537)
中保存了数字 65537(即 e 的值)。
例如:
1 2 3 4 5 6 7 8 9 import retext = 'N = 1234567890' match = re.search(r'N\s*=\s*(\d+)' , text)n = match .group(1 ) print (n)
在上面代码中,(\d+)
匹配文本中的数字字符,并将该数字字符作为一个捕获组进行捕获。由于该捕获组是第一个捕获组,因此可以通过 group(1)
获取该组的匹配结果,即其中的数字 1234567890。
当然,对于这个题目给定了输出,可以直接按照题目的格式匹配:
1 2 3 4 5 6 7 8 9 n_and_e = p.recvuntil(b'[+]input your option:' ).decode() n_match = re.search(r'N = (\d+)' , n_and_e) n = int (n_match.group(1 )) e_match = re.search(r'e = (\d+)' , n_and_e) e = int (e_match.group(1 )) print (f'n = {n} ' ) print (f'e = {e} ' )