Python学习日志13 - 正则表达式

Posted RBHGO_49

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python学习日志13 - 正则表达式相关的知识,希望对你有一定的参考价值。

Python学习日志

RBHGO的主页欢迎关注

温馨提示:创作不易,如有转载,注明出处,感谢配合~

目录

前言

上次分享了如何使用Python实现办公自动化,基于我们的日常学习工作,它的用处是还是非常大的,我分享了很大一部分文档的操作方法办公自动化有兴趣的话可以去阅读一下,最好还能点个赞,帮我指错哦。在这里就通过一个例题稍微回顾,然后开始今天的分享内容,我们先来看例题。

1.将三个Excel文件的内容合并到一个Excel文件中
2.将合并后的Excel文件转换成一个CSV文件

"""
1.将三个Excel文件的内容合并到一个Excel文件中

Author: RBHGO
"""
import openpyxl
from openpyxl.styles import Font, Alignment, Border, Side


sum_wb = openpyxl.Workbook()
sum_sheet = sum_wb.active
sum_sheet.title = '销售汇总数据'
row_index = 2
# 改变单元格样式
sum_sheet.append(('药房2018年销售数据汇总', ))
cell = sum_sheet['A1']  # type: Cell
cell.alignment = Alignment(horizontal='center')

# 合并单元格
sum_sheet.merge_cells('A1:G1')
sum_sheet.append(('购药时间', '社保卡号', '商品编码',
                  '商品名称', '销售数量', '应收金额',
                  '实收金额'))


names = ('高新', '新津', '犀浦')
for name in names:
    wb = openpyxl.load_workbook(f'resources/药房({name}店)2018年销售数据.xlsx')
    sheet = wb.worksheets[0]
    flag = True
    for row in sheet.iter_rows(min_row=3):
        for col_index, cell in enumerate(row):
            if cell.value is None:
                flag = False
                break
            sum_sheet.cell(row_index, col_index + 1, cell.value)
        if not flag:
            break
        row_index += 1
sum_wb.save('resources/药房2018年销售数据汇总.xlsx')
"""
2.将合并后的Excel文件转换成一个CSV文件

Author: RBHGO
"""
import csv

import openpyxl

workbook = openpyxl.load_workbook('resources/药房2018年销售数据汇总.xlsx')
worksheet = workbook.worksheets[0]
data = [[cell.value for cell in row]
        for row in worksheet[f'A3:G{worksheet.max_row}']]
with open('resources/sales_data.csv', 'w', encoding='utf-8') as file:
    writer = csv.writer(file)
    writer.writerow(('购药时间', '社保卡号', '商品编码',
                     '商品名称', '销售数量', '应收金额',
                     '实收金额'))
    writer.writerows(data)

进入正题

在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。(正则表达式就是记录文本规则的代码。)换句话说,我们可以使用正则表达式来定义字符串的匹配模式,即如何检查一个字符串是否有跟某种模式匹配的部分或者从一个字符串中将与模式匹配的部分提取出来或者替换掉再或者进行拆分。

如果您是之前完全没有听说过正则表达式也给大家推荐一篇博文《正则表达式30分钟入门教程》,我接触时也是先看过这片文章后,在通过对正则表达式进行应用学习的,里面的例子比较经典,稍加思考基本都还算是好理解的,同时对正则表达式中的一些基本符号也会有一定的了解。

Python学习日志13课 - 正则表达式

首先关于正则表达式的相关知识肯定是要提基本符号的,这里是我对正则表达式中的一些基本符号进行的稍微总结。有一条比其他规则的优先级更高:最先开始的匹配拥有最高的优先权

基本符号表

符号/代码/语法作用例子说明
\\d匹配数字\\d\\d匹配01-99的任意数字
\\s匹配任意空白字符I\\sdo匹配 I do
\\b匹配单词的开始或结束(如果在字符类里使用代表退格)\\bhello\\b匹配单词hello
\\w匹配字母/数字/下划线r\\w匹配 ru、r1、r_
^匹配字符串的开始^I匹配I开头的字符串
$匹配字符串的结束you$匹配you结尾的字符串
.匹配任意字符m.匹配mm、m1、m#、m_
*重复0次或者更多次.*匹配任意字符0-无数次
+重复1次或者更多次.+匹配任意字符1-更多次
?重复0次或者1次.?匹配任意字符0或1
{x}重复x次{9}匹配重复9次
{x,}重复x次或者更多次{7}匹配重复7次及以上
{x,y}重复不小于x次不大于y次{3,5}匹配不小于3不大于5次
[]匹配来自字符集的任意单一字符例子如下5条
[0-9]匹配一位数基本上等于\\d
[aeiou]匹配元音字符
[.!@#_%]匹配里面的符号
[a-z0-9A-Z_]匹配字母/数字/下划线基本上等于\\w
[\\u4e00-\\u9fa5]匹配绝大部分汉字
[^]匹配不在字符集中的任意单一字符基本上是和[]相反,例子如下
[^aeiou]可以匹配任一非元音字母字符
\\W匹配非字母/数字/下划线r\\W不匹配 ru、r1、r_,但匹配r@、r#
\\S匹配非空白字符I\\Sdo不匹配 I do,但匹配I#do
\\D匹配非数字\\D匹配a、_、#
\\B匹配不是单词开头或结束的位置\\Bhello\\B匹配不是单词hello开头或结束的位置
|分支foo|bar可以匹配foo或者bar
(?#)注释这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读
()匹配这个子表达式的文本(也就是此分组捕获的内容)从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推
(exp)匹配exp并捕获文本到自动命名的组里未命名情况下(0)参数为0是所有分组内容,(1)捕获的第一个组
(?<name>exp)匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp)命名用<>''一样
(?:exp)匹配exp,不捕获匹配的文本,也不给此分组分配组号
(?=exp)匹配exp前面的位置\\b\\w+(?=ing)可以匹配I'm dancing中的danc
(?<=exp)匹配exp后面的位置(?<=\\bdanc)\\w+\\b可以匹配I love dancing and reading中的第一个ing
(?!exp)匹配后面跟的不是exp的位置
(?<!exp)匹配前面不是exp的位置
*?重复任意次,但尽可能少重复.*?匹配任意字符0-无数次,但尽可能少重复
+?重复1次或更多次,但尽可能少重复.+?匹配任意字符1-无数次,但尽可能少重复
??重复0次或1次,但尽可能少重复.??匹配任意字符0或1,但尽可能少重复
{n,m}?重复n到m次,但尽可能少重复{3,5}?重复3到5次,但尽可能少重复
{n,}?重复n次以上,但尽可能少重复{7,}?重复7次以上,但尽可能少重复

**说明:**如果需要匹配的字符是正则表达式中的特殊字符,那么可以使用\\进行转义处理,例如想匹配\\/则要写成\\\\\\/的形式;同理,想匹配圆括号必须写成\\(\\),否则圆括号被视为正则表达式中的分组。当然在Python的字符串中我们也可以使用原始字符r'字符串'这样就不需要转义处理。

Python对正则表达式的支持

Python提供了re模块来支持正则表达式相关操作,下面是re模块中的核心函数。

函数说明
compile(pattern, flags=0)编译正则表达式返回正则表达式对象
match(pattern, string, flags=0)用正则表达式匹配字符串 成功返回匹配对象 否则返回None
search(pattern, string, flags=0)搜索字符串中第一次出现正则表达式的模式 成功返回匹配对象 否则返回None
split(pattern, string, maxsplit=0, flags=0)用正则表达式指定的模式分隔符拆分字符串 返回列表
sub(pattern, repl, string, count=0, flags=0)用指定的字符串替换原字符串中与正则表达式匹配的模式 可以用count指定替换的次数
fullmatch(pattern, string, flags=0)match函数的完全匹配(从字符串开头到结尾)版本
findall(pattern, string, flags=0)查找字符串所有与正则表达式匹配的模式 返回字符串的列表
finditer(pattern, string, flags=0)查找字符串所有与正则表达式匹配的模式 返回一个迭代器
purge()清除隐式编译的正则表达式的缓存
re.I / re.IGNORECASE忽略大小写匹配标记
re.M / re.MULTILINE多行匹配标记

说明: 上面提到的re模块中的这些函数和方法,实际开发中也可以用正则表达式对象的方法替代对这些函数的使用,如果一个正则表达式需要重复的使用,那么先通过compile函数编译正则表达式并创建出正则表达式对象无疑是更佳的选择。

例 题

(1).用正则表达式分别验证QQ号和用户名

这道题主要是介绍re模块中的matchfullmatch函数的应用场景

"""
正则表达式(regular expression)
正则表达式 ---> 模式 ---> 匹配字符串的模式 ---> 复杂的匹配规则

Python使用正则表达式的两种方式:
 ~ 不创建正则表达式对象,直接调用函数进行匹配操作
 ~ 创建正则表达式对象(Pattern),通过给对象发消息实现匹配操作
 
match - 匹配 - 从头开始进行匹配
fullmatch - 匹配 - 完全匹配

Author: RBHGO
Declaration: Mia San Mia ~~~
"""
import re

qq = input('请输入您的QQ号:')
# r原始字符,match如果要完全匹配使用^$
matcher = re.match(r'^[1-9]\\d{4,}$', qq)
# fullmatch完全匹配,不用^$
# matcher = re.fullmatch(r'\\d{4,12}', qq)
if matcher is None:
    print('QQ号不合规则!!!')
else:
    print('QQ号可以使用')


username = input('请输入用户名:')
username_pattern = re.compile(r'^\\w{6,20}$')
print(type(username_pattern))
matcher = username_pattern.match(username)
print(type(matcher))
if matcher is None:
    print('无效用户名!!!')
else:
    print(matcher.group())
(2).用正则表达式找到所给字符串中所有的数字

这道题主要是介绍re模块中的searchfindall函数的应用场景

"""
正则表达式(regular expression)

match - 匹配 - 从头开始进行匹配
search - 搜索 - 从任意位詈匹配
findall - 从字符串中找出所有和正则表达式匹配的内容 ---> 返回list[str]

Author: RBHGO
"""
import re

content = """报警电话:110,我们班是Python-817班,
我的QQ号是9023758,我的手机号是19105242466,谢谢!"""

# # match, fullmatch在用来搜索的就不管用了
pattern = re.compile(r'\\d+')
matcher = pattern.search(content)
while matcher:
    print(matcher.group())
    # 输出匹配字符串的开始/结束的位置
    # print(matcher.start(), matcher.end())
    # 创建对象,search函数中没有,search方法中有位置参数pos
    matcher = pattern.search(content, matcher.end())

# 得到字符串中的匹配结果,返回一个列表,可以将元素遍历出来
print(pattern.findall(content))
(3).用正则表达式匹配新闻的标题及链接

这里主要是为了提到贪婪和惰性机制,以及捕获组的应用

"""
正则表达式(regular expression)
懒惰(量词加?)/贪婪(.*)规则
括号()得到捕获组,用于捕获想要内容

Author: RBHGO
"""

import re
import requests

# 惰性匹配,量词后面加?,尽可能短的匹配

# 匹配整个a标签,但是只捕获()中的内容 ---> 正则表达式的捕获组
pattern = re.compile(r'<a\\s.*?href="(.+?)".*?title="(.+?)".*?>')
resp = requests.get('https://www.sohu.com/')
# 捕获得到2元组
results = pattern.findall(resp.text)
# 将二元组中的元素先后遍历出来
for href, title in results:
    print(title)
    if not href.startswith('https://www.sohu.com'):
        href = 'https://www.sohu.com' + href
    print(href)
(4).替换字符串中的不良内容

re模块的正则表达式相关函数中都有一个flags参数,它代表了正则表达式的匹配标记,可以通过该标记来指定匹配时是否忽略大小写、是否进行多行匹配、是否显示调试信息等。

"""
正则表达式 - 替换

Author: RBHGO
"""
# 不良内容(敏感内容)过滤-> 替换 -> sub(substitute)
import re

content = '傻逼,FUck you,son of bitch'
modified_content = re.sub(r'[傻沙煞][逼笔币]|fuck|shit|son\\sof\\sbitch',
                          '***', content, flags=re.IGNORECASE)  # re.I
print(modified_content)
(5).拆分长字符串
"""
正则表达式 - 拆分字符串

Author: RBHGO
"""
import re

poem = '风急天高猿啸哀,渚青沙白鸟飞回。无边落木萧萧下,不尽长江滚滚来。'
# 拆分过后得到字符串拆分为多个元素的列表
sentences_list = re.split(r'[,。,.]', poem)
print(sentences_list)
# 拆分会多出一个空字符,生成式判断去掉
sentences_list = [sentence for sentence in sentences_list if sentence]
print(sentences_list)
for sentence in sentences_list:
    print(sentence)

总结

看完上面的例题,相信大家或多或少的都明白正则表达式在字符串的处理和匹配上真的非常强大,只是想写好正则表达式确实也比较难,对于新手来说更是不那么‘友善’。当然和所有的知识都是一样的 —— 熟能生巧,放开手来尝试很重要,越写得多用起来也就越得心应手。

感谢学习陪伴,您的点赞,评论就是我更新的动力

以上是关于Python学习日志13 - 正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

Python学习笔记之正则表达式

Python 正则表达式从系统日志服务器中提取 FQDN

python基础学习(十三)

python基础学习笔记(十三)

循环通过 python 正则表达式匹配

第43天python学习re模块学习