一、正则表达式的概念
正则表达式是一种"匹配模式的语言",它用来告诉计算机我们想在一段文字中找到什么样的内容。是一种非常高级的搜索工具。
正则表达式的基本组成部分:
1、普通字符:直接匹配自身的字符 ,比如w匹配字符'w'
2、元字符(Metacharacters):具有特殊含义的字符。
(1) '.' 匹配除换行符以外的任意单个字符 例如:a.b 匹配acb、a1b、a@b等
(2)'^' 匹配字符串的开头 例如: ^hello - 只有以hello开头才匹配
(3)'' 匹配字符串的结尾 例如:world 只有以world结尾才匹配
(4)'*' 前一个字符出现0次或多次 例如:ab* 匹配 a、ab、abbb等
(5)'+' 前一个字符出现1次或多次 例如:ab+ 匹配 ab、abb、abbb等
(6)'?' 前一个字符出现0次或一次 例如 ab?匹配 a、ab
(7) ' ' 匹配括号里任意一个字符 例如 abc 匹配a、b、c其中一个
(8) ' | ' 逻辑或 例如a|b 匹配a或者b
(9)'()' 分组、提取内容、改变优先级 例如 (ab)+ → 匹配 ab、abab、ababab
3、转义字符:用于将元字符当作普通字符对待,通常用反斜杠 \ 如\.匹配一个点
4、量词:指定匹配的数量。 {n}:精确匹配n次 {n,} 至少匹配n次 {n,m}:匹配n到m次。
示例1 查找特定单词
welcome to beijing, beijing is the capital of china
如果我想查找所以的beijing单词,我可以用beijing这个正则表达式来做这件事,正则表达式会匹配并找到所有beijing这个单词
import re
text1 = 'welcome to beijing, beijing is the capital of china'
result = re.findall(r'beijing', text1)
print(result)
示例2:灵活匹配不同的单词
假如我想找beijing 还想找beijingtiananmen 那可以使用beijing(tiananmen)? 这个正则表达式
import re
text1 = 'welcome to beijing, beijing is the capital of china,beijingtiananmen very beautiful'
result = re.findall(r'beijing(?:tiananmen)?', text1)
print(result)
findall 只返回捕获组
将捕获组
()改为非捕获组(?:),这样findall就会返回整个正则的匹配结果
示例3:匹配邮件地址
import re
text2= 'email: 123456@qq.com, 123abc@163.com, 1wwq6@126.com'
email = re.findall(r'\w+@\w+\.\w+',text2)
print(email)
\w+ 匹配1个或多个字母、数字或下划线
A-Za-z0-9._%+-+@A-Za-z0-9.-+\.A-Za-z{2,4} 这种方法更精确
为什么要学正则表达式?
提高工作效率:在文本处理任务中,使用正则表达式可以显著减少代码量,并且能够快速、准确地处理数据。
通用性强:正则表达式不仅适用于Python,还能在许多编程语言中使用,如JavaScript、Java等,甚至在一些文本编辑器中也可以使用。
解决复杂问题:当你需要从大量文本中提取特定信息,或进行复杂的搜索和替换时,正则表达式是非常有效的工具。
二、正则表达式基础
1、字符匹配
import re
text1 = "hello"
text2 = "h.llo"
match = re.match(text2,text1)
if match:
print(match.group())
else:
print("no match")
输出 hello
代码解释:
text1 = "hello":定义了一个待匹配的字符串 text1。
text2 = "h.llo":定义了一个正则表达式模式 text2。这里的 . 是一个特殊字符,表示可以匹配除换行符之外的任何一个字符。所以,这个模式可以匹配 "hello",也可以匹配 "hallo",但不能匹配 "hlllo"。
match = re.match(text2, text1):使用 re.match() 函数尝试将模式 pattern 从字符串 text 的开头进行匹配。如果匹配成功,返回一个匹配对象,否则返回 None。
if match::检查是否有匹配对象。如果有,表示匹配成功。
print("匹配成功:", match.group()):如果匹配成功,打印匹配的内容。match.group() 返回匹配的字符串。
else: print("匹配失败"):如果没有匹配成功,则打印 "匹配失败"。
2、字符集
import re
text1 = "hHello"
print(re.match(text1,"hello"))
print(re.match(text1,"Hello"))
这里的 "hH" 表示一个字符集,h 或 H 都可以匹配。正则表达式要求字符串以 "h" 或 "H" 开头,后面跟着 "ello"。
3、重复匹配
import re
text1 = "a+b*c"
print(re.match(text1,"aabbc"))
print(re.match(text1,"ac"))
a+b*c意思就是 有一个或多个a 0个或多个b 一个c (abc顺序不能乱哦)
4、特殊字符
import re
text1 = r"\d+\s\w+"
text2 = "123 abc"
r= raw string(原始字符串) 告诉 Python:不要把反斜杠\当成转义符,就当普通字符。\d+ : 匹配一个或多个数字
\s : 匹配一个空白字符
\w+: 匹配一个或多个字母、数字或下划线
匹配时严格按照顺序关系。
5、边界匹配
import re
text1 = "^hello"
print(re.match(text1, "hello world"))
print(re.match(text1, "world hello"))
匹配以字符串hello开头的
6、常用的正则表达式函数
1、re.match() 从字符串开头开始匹配
import re
text1 = r"\d{3}-\d{3,8}"
print(re.match(text1, "010-12345")) 匹配
print(re.match(text1, "01022-1234")) 不匹配
这个意思就是匹配3位数字加-加3-8位的数字
\d
• 含义:\d 是一个预定义字符类,表示匹配任意一个数字字符(即 0-9)。
• 等价于:0-9。
{3}
• 含义:花括号 {n} 表示前面的元素必须出现恰好 n 次。
• 在这里:\d{3} 表示匹配恰好三个连续的数字。
\d{3,8}
• 含义:\d{3,8} 表示匹配 3 到 8 个连续的数字。
• 解释:花括号 {n,m} 表示前面的元素可以出现至少 n 次,最多 m 次。在这里,
\d{3,8} 表示可以匹配 3 到 8 个数字。
2、re.search()
re.search()方法用于搜索字符串中首次出现的匹配项。与match()不同,search()可以匹配字符串任意位置
import retext1 = r"\d{3}-\d{3,8}"
print(re.search(text1, "my 010-12345 but your 012-123456"))
只匹配第一个010-12345
3、re.findall()
re.find()方法用于查找字符串中所有非折叠的匹配项,并以列表形式返回
import retext1 = r"\d{3}-\d{3,8}"
print(re.findall(text1, "my 010-12345 but your 012-123456"))
返回:'010-12345', '012-123456'
• re.findall() 返回所有匹配项的列表。如果没有匹配项,则返回空列表。
假设我们有一个字符串 "ababa",我们想要找到 "aba" 这个模式。
import retext1 = "aba"
text2 = "ababa"
print(re.findall(text1, text2))
输出'aba'
解析:
在字符串 "ababa" 中,虽然有两个 "aba" 出现的可能性(第一个从索引 0 开始,第
二个从索引 2 开始),但 re.findall() 只会匹配第一次出现的 "aba",然后继续从匹配
后的字符(即从索引 3 开始)进行查找。由于从索引 3 开始再没有 "aba" 出现,所
以结果中只有一个匹配。
4. re.sub()
re.sub()方法用于替换字符串中所有匹配的子字符串。它的语法为re.sub(pattern,repl,string,count=0), 其中pattern是正则表达式,repl是替换内容,string是要处理的字符串,用于指定替换的最大次数(默认为0,表示替换所有匹配项)。
import retext1 = r"\d{3}-\d{3,8}"
text2 = "my 010-12345 but your 012-123456"
print(re.sub(text1, "000-0000", text2,count=1))、
把匹配到的替换成000-0000 count1代表匹配1次
my 000-0000 but your 012-123456
5、re.split()
re.split() 方法根据模式中的匹配项来拆分字符串,并返回拆分后的列表
import retext1 = r"\s+"
text2 = "This is a simple sentence."
words = re.split(text1, text2)
print("拆分后的列表:", words) # 输出 "This", "is", "a", "simple", "sentence."
s+是匹配1个或多个空白符
6. re.compile()
re.compile() 方法用于预编译正则表达式。预编译的正则表达式对象可以复用,以提高效率,
特别是在多次使用同一正则表达式时。
代码示例:
import re编译正则表达式
pattern = re.compile(r"\d{3}-\d{3,8}")
使用编译后的模式
text = "My number is 123-4567 and my friend's number is 987-6543210."
matches = pattern.findall(text)
print("找到的所有匹配项:", matches) # 输出 "123-4567", "987-6543210"
继续使用编译后的模式进行匹配
match = pattern.search(text)
if match:
print("首次匹配:", match.group()) # 输出 "123-4567"
解释:
• re.compile() 返回一个正则表达式对象,该对象可以使用 match(), search(), findall(),
sub() 等方法。
三、实战案例
1、假设你有一个Apache日志文件access.log,内容如下:
192.168.0.1 - - 28/Aug/2030:10:22:04 +0000 "GET /index.html HTTP/1.1" 200 2326
203.0.113.45 - - 28/Aug/2030:10:22:05 +0000 "POST /login.php HTTP/1.1" 200 342
192.168.0.2 - - 28/Aug/2030:10:22:06 +0000 "GET /dashboard HTTP/1.1" 200 128我要过滤出ip地址
import re
with open('file/access.log','r') as f:
for line in f:
xx = re.split(r"\s",line)
ip = xx0
print(ip)
输出 :
192.168.0.1
203.0.113.45
192.168.0.2
2、假设有两个数据库配置文件 我想修改两个文件中的用户名
config1.conf
DB_CONNECTION="mysql://user1:password@localhost/db1"
config2.conf
DB_CONNECTION="mysql://user1:password@localhost/db2"
代码:
import reconfig_files = "file/config1.conf","file/config2.conf"
pattern = r'(DB_CONNECTION="mysql://)(\w+)(:password@localhost/db12")'
new_name = 'admin'
for file in config_files:
with open(file,"r") as f:
content = f.read()
new_content = re.sub(pattern,r'\1'+new_name+r'\3',content)
with open(file,"w") as f:
f.write(new_content)
print("file {} has been updated".format(file))
解析:
r'\1' + new_name + r'\3' 替换规则
\1 = 保留第 1 段不动
new_name = 把中间的用户名改成 admin
\3 = 保留第 3 段不动3、 检查配置文件中的缺失项
我想检查配置文件是否都包含max_connection 这个设置项
import re
config_files = "file/config1.conf","file/config2.conf"
pattern = r"max_connections\s*=\s*(\d+)"
for file in config_files:
with open(file,"r") as f:
content = f.read()
pz = re.search(pattern,content)
if pz:
print("{file} have mc")
else:
print("{file} have not mc")
解析:
\s*= 0 个 或 多个 空格- 意思就是:等号前面有没有空格都能匹配