parse库,一个优雅的python库

前言

Python中,format方法和f-strings是两种常用的字符串插值方法。

name = "Haige"
age = "18"
print(f"{name} is {age} years old.")# Haige is 18 years old.

而如果是要从字符串中提取期望的值呢?相信很多人的第一或第二想法是使用正则表达式。

熟悉正则表达式的人都明白,学习起来并不困难,写起来也相对容易。

然而,正则表达式几乎不具备可读性,维护起来确实令人头痛。别以为你写的那段正则表达式可以轻易驾驭它,过了一段时间你可能都无法认识它了。

可以毫不夸张地说,对于许多人来说,正则表达式是一种痛苦的经历。

今天,我要介绍给你一个解放你的好工具,让你远离正则表达式的噩梦,那就是 Python 中一个"鲜为人知"的库——parse

Github地址:https://github.com/r1chardj0n3s/parse

初体验

假设我们有一组文本,其中包含了一些格式化的信息,比如日期、时间和事件。我们想要从这些文本中提取出这些信息。

text = "Event: Meeting Date: 2023-01-15 Time: 14:30 Location: Conference Room"

如果是你,你会怎么做呢?也许上来就是正则一把梭了。就像这样:

import retext = "Event: Meeting Date: 2023-01-15 Time: 14:30 Location: Conference Room"# 定义正则表达式模式
pattern = r"Event: (.+) Date: (\d{4}-\d{2}-\d{2}) Time: (\d{2}:\d{2}) Location: (.+)"# 匹配文本
match = re.match(pattern, text)if match:event = match.group(1)date = match.group(2)time = match.group(3)location = match.group(4)print("Event:", event)print("Date:", date)print("Time:", time)print("Location:", location)
else:print("Failed to match the text")

输出结果:

Event: Meeting
Date: 2023-01-15
Time: 14:30
Location: Conference Room

不过,也许你该试试parse库。

首先,我们需要安装 parse 库,可以通过 pip 进行安装:

pip install parse

我们想要从这个文本中提取出事件名称、日期、时间和地点。下面是使用 parse 库的方法:

from parse import parse# 定义模板
template = "Event: {} Date: {} Time: {} Location: {}"# 解析文本
result = parse(template, text)if result:event, date, time, location = resultprint("Event:", event)print("Date:", date)print("Time:", time)print("Location:", location)
else:print("Failed to parse the text")

输出结果:

Event: Meeting
Date: 2023-01-15
Time: 14:30
Location: Conference Room

在这个示例中,我们首先定义了一个模板,模板中包含了我们要提取的信息的格式。然后,我们使用 parse 函数解析文本,如果解析成功,返回的结果是一个元组,包含了提取出的信息。最后,我们成功地从文本中提取出了事件名称、日期、时间和地点。

通过对比可以看出,使用parse库进行字符串解析相对更加简洁、清晰,并且不容易出错。
而使用正则表达式虽然也可以完成相同的任务,但是需要编写更长的模式,并且容易出现错误,没有一丝美感可言

parse库的基本用法

parse 的结果

parse的结果只有两种结果:

  • 没有匹配上,parse的值为None
  • 如果匹配上,parse的值则为Result实例
from parse import parse# 示例字符串
log_string = '192.168.0.1 - - [05/Feb/2024:12:30:45 +0800] "GET /index.html HTTP/1.1" 200 1234'# 定义解析模式
pattern = '{ip} - - [{timestamp}] "{method} {url}" {status_code} {response_size}'# 解析字符串
result = parse(pattern, log_string)# 输出解析结果
if result:print("IP:", result['ip'])print("Timestamp:", result['timestamp'])print("Method:", result['method'])print("URL:", result['url'])print("Status Code:", result['status_code'])print("Response Size:", result['response_size'])
else:print("匹配失败")

如果我们将正确格式的日志字符串作为输入,将会得到匹配成功的结果:

IP: 192.168.0.1
Timestamp: 05/Feb/2024:12:30:45 +0800
Method: GET
URL: /index.html
Status Code: 200
Response Size: 1234

常用方法

让我来逐个解释并举例说明 parse 库中的 searchfindallcompilewith_pattern 方法的用法。

1. search 方法

search 方法用于在字符串中搜索与指定模式匹配的第一个结果,并返回解析结果。

from parse import search# 示例字符串
text = "The price of the apple is $2.50."# 定义解析模式
pattern = "The price of the {fruit} is ${price}."# 使用 search 方法解析字符串
result = search(pattern, text)# 访问解析结果
if result:print("Fruit:", result['fruit'])print("Price:", result['price'])
else:print("未找到匹配项")

输出结果:

Fruit: apple
Price: 2
2. findall 方法

findall 方法用于在字符串中搜索所有与指定模式匹配的结果,并返回解析结果列表。

from parse import findall# 示例字符串
text = "The prices are $2.50, $3.00, and $4.25."# 定义解析模式
pattern = "${price:.2f}"  # 使用 ":.2f" 匹配包含两位小数的浮点数# 使用 findall 方法解析字符串
results = findall(pattern, text)# 访问解析结果
if results:for idx, price in enumerate(results, start=1):# 将小数部分格式化为两位formatted_price = "{:.2f}".format(price['price'])print(f"Price {idx}: {formatted_price}")
else:print("未找到匹配项")

输出结果:

Price 1: 2.50
Price 2: 3.00
Price 3: 4.25
3. compile 方法

compile 方法用于将解析模式编译为可重复使用的解析器对象。

from parse import compile# 定义解析模式
pattern = "The price of the {fruit} is ${price}."# 编译解析模式
parser = compile(pattern)# 使用编译后的解析器对象解析字符串
result = parser.parse("The price of the apple is $2.50.")# 访问解析结果
if result:print("Fruit:", result['fruit'])print("Price:", result['price'])
else:print("未找到匹配项")

输出结果:

Fruit: apple
Price: 2.50
4. with_pattern 方法

with_pattern 方法用于绑定解析模式与要解析的字符串,并返回一个解析结果对象。

from parse import Parser, with_pattern@with_pattern(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b')
def email(text: str) -> str:return textcompiler = Parser("my email address is {email:Email}", dict(Email=email))legal_result = compiler.parse("my email address is xx@xxx.com")  # legal email
illegal_result = compiler.parse("my email address is xx@xx")     # illegal emailprint(legal_result["email"])
print(illegal_result)

输出结果:

xx@xxx.com
None

捕获组:

from parse import *@with_pattern(r'((\d+))', regex_group_count=2)
def parse_number2(text):return int(text)obj = parse('Answer: {:Number2} {:Number2}', 'Answer: 42 43', dict(Number2=parse_number2))
print(obj)  # <Result (42, 43) {}>

将输入的文本转换为布尔值:

from parse import *yesno_mapping = {"yes":  True,   "no":    False,"on":   True,   "off":   False,"true": True,   "false": False,
}@with_pattern(r"|".join(yesno_mapping))
def parse_yesno(text):return yesno_mapping[text.lower()]obj = parse('Answer: {:bool}', 'Answer: yes', dict(bool=parse_yesno))
print(obj)  # <Result (True,) {}>obj2 = parse('Answer: {:bool}', 'Answer: off', dict(bool=parse_yesno))
print(obj2)  # <Result (False,) {}>

匹配并类型转换

有的时候,我们希望提取的时候就按照我们的类型进行转换。

from parse import Parser# 创建解析器对象并指定解析模式
parser = Parser("I have {count:d} apples", {})# 示例字符串
text = "I have 5 apples"# 使用解析器对象解析字符串
result = parser.parse(text)# 访问解析结果
if result:count = result['count']print("Number of apples:", count)print("Type of count:", type(count))
else:print("未找到匹配项")

输出结果:

Number of apples: 5
Type of count: <class 'int'>

匹配时间:

from parse import parsedatetime = parse("{:%Y-%m-%d %H:%M:%S}", "2023-11-23 12:56:47")
print(datetime)  # <Result (datetime.datetime(2023, 11, 23, 12, 56, 47),) {}>
print(datetime[0])  # 2023-11-23 12:56:47

更多类型请参考官方文档:

特殊对齐

from parse import *text = "hello     world    , hello python"# 右对齐
print(parse('hello {:>} , hello python', text))
# 左对齐
print(parse('hello {:<} , hello python', text))
# 居中对齐
print(parse('hello {:^} , hello python', text))
print(parse('hello{:^} , hello python', text))

输出结果:

<Result ('world   ',) {}>
<Result ('    world',) {}>
<Result ('world',) {}>
<Result ('world',) {}>
<Result ('world',) {}>

大小写敏感开关

parse库默认是不区分大小写的。如果需要开启大小写敏感模式,可以通过设置case_sensitive参数为True来实现。

from parse import parse# 原始字符串
file_name = "document.TXT"# 解析文件名,大小写敏感
result_sensitive = parse("{name}.txt", file_name, case_sensitive=True)
print(result_sensitive)  # 输出为 None,因为大小写不匹配# 解析文件名,大小写不敏感
result_insensitive = parse("{name}.txt", file_name, case_sensitive=False)
print(result_insensitive)  # 输出为 ParseResult([('name', 'document')]),大小写不敏感匹配成功

匹配字符数

宽度和精度可用于限制输入匹配文本的大小。宽度指定最小尺寸,精度指定最大尺寸。例如:

from parse import parseparse('{:.2}{:.2}', 'look')  # 指定精度print(parse('{:4}{:4}', 'look at that'))  # 指定宽度print(parse('{:4}{:.4}', 'look at that'))  # 同时指定print(parse('{:2d}{:2d}', '0440'))

输出结果:

<Result ('look', 'at that') {}>
<Result ('look at ', 'that') {}>
<Result (4, 40) {}>

三个重要属性

  • fixed:利用位置提取的匿名字段的元组。
  • named:存放有命名的字段的字典。
  • spans:存放匹配到字段的位置。
from parse import parseprofile = parse("I am {name}, {age:d} years old, {}", "I am Jack, 27 years old, male")
print(profile.fixed)print(profile.named)print(profile.spans)

输出结果:

('male',)
{'name': 'Jack', 'age': 27}
{'name': (5, 9), 'age': (11, 13), 0: (25, 29)}

自定义类型转换

from parse import parsedef custom_upper(string):return string.upper() + " HAIGE"print(parse('{:my_upper} world', 'hello world', dict(my_upper=custom_upper)))

输出结果:

<Result ('HELLO HAIGE',) {}>

使用场景

解析nginx日志

#!usr/bin/env python
# -*- coding:utf-8 _*-
# __author__:lianhaifeng
# __time__:2024/2/7 20:02
from parse import parse
import json
import pandas as pd
from typing import Listdef parse_nginx_log(log_lines):template = '{ip} - - [{timestamp}] "{method} {path} HTTP/{http_version}" {status_code} {response_size} "{user_agent}"'data = []for log_line in log_lines:result = parse(template, log_line)if result:data.append({'ip': result['ip'],'timestamp': result['timestamp'],'method': result['method'],'path': result['path'],'http_version': result['http_version'],'status_code': int(result['status_code']),'response_size': int(result['response_size']),'user_agent': result['user_agent']})return datadef build_dataframe(records: List[dict]) -> pd.DataFrame:result: pd.DataFrame = pd.DataFrame.from_records(records, index='ip')return resultnginx_log = ['127.0.0.1 - - [01/Jan/2022:12:00:00 +0000] "GET /index.html HTTP/1.1" 200 1234 "-"','127.0.0.1 - - [01/Jan/2022:16:00:00 +0000] "GET /index.html HTTP/1.1" 200 1234 "-"','192.168.1.5 - - [01/Jan/2023:12:03:00 +0000] "GET /index3.html HTTP/1.1" 200 1236 "-"','192.168.18.36 - - [01/Jan/2024:11:23:00 +0000] "GET /index2.html HTTP/1.1" 200 3234 "-"'
]parsed_log = parse_nginx_log(nginx_log)if parsed_log:json_log = json.dumps(parsed_log)# print(json_log)print(build_dataframe(parsed_log))
else:print('Failed to parse the log line')

输出结果

                                timestamp method  ... response_size user_agent
ip                                                ...                         
127.0.0.1      01/Jan/2022:12:00:00 +0000    GET  ...          1234          -
127.0.0.1      01/Jan/2022:16:00:00 +0000    GET  ...          1234          -
192.168.1.5    01/Jan/2023:12:03:00 +0000    GET  ...          1236          -
192.168.18.36  01/Jan/2024:11:23:00 +0000    GET  ...          3234          -[4 rows x 7 columns]

解析配置文件中的键值对:

from parse import parse# 定义配置文件模板
template = "{key}={value}"# 解析配置文件行
result = parse(template, "debug=True")if result:key = result['key']value = result['value']print(f"Key: {key}, Value: {value}")  # Key: debug, Value: True

小结

在字符串解析处理中,parse库提供了极大的便利。相较于使用正则表达式 (re),parse简化了模式的定义和匹配过程,极大地提高了开发效率。

在一些简单的场景中,使用parse可比使用re去写正则表达式高出几个level。使用parse编写的代码富有美感,可读性极高,后期维护起来也毫无压力。

综上所述,强烈推荐您在Python开发中使用parse库,它能够让您的代码更加优雅、高效、易读易维护。

更多parse库用法请翻阅官方文档…

最后

今天的分享就到这里。如果觉得不错,点赞关注安排起来吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/459555.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

假期作业5

TCP和UDP区别 TCP ----稳定 1、提供面向连接的&#xff0c;可靠的数据传输服务&#xff1b; 2、传输过程中&#xff0c;数据无误、数据无丢失、数据无失序、数据无重复&#xff1b; 3、数据传输效率低&#xff0c;耗费资源多&#xff1b; 4、数据收发是不同步的&#xff1b; U…

【Flink入门修炼】1-3 Flink WordCount 入门实现

本篇文章将带大家运行 Flink 最简单的程序 WordCount。先实践后理论&#xff0c;对其基本输入输出、编程代码有初步了解&#xff0c;后续篇章再对 Flink 的各种概念和架构进行介绍。 下面将从创建项目开始&#xff0c;介绍如何创建出一个 Flink 项目&#xff1b;然后从 DataStr…

flutter开发实战-ijkplayer视频播放器功能

flutter开发实战-ijkplayer视频播放器功能 使用better_player播放器进行播放视频时候&#xff0c;在Android上会出现解码失败的问题&#xff0c;better_player使用的是video_player&#xff0c;video_player很多视频无法解码。最终采用ijkplayer播放器插件&#xff0c;在flutt…

[机器学习]K-means——聚类算法

一.K-means算法概念 二.代码实现 # 0. 引入依赖 import numpy as np import matplotlib.pyplot as plt # 画图依赖 from sklearn.datasets import make_blobs # 从sklearn中直接生成聚类数据# 1. 数据加载 # 生成&#xff08;n_samples&#xff1a;样本点&#xff0c;centers&…

06-OpenFeign-使用HtppClient连接池

默认下OpenFeign使用URLConnection 请求连接&#xff0c;每次都需要创建、销毁连接 1、添加ApacheHttpClient依赖 <!-- 使用Apache HttpClient替换Feign原生httpclient--><dependency><groupId>org.apache.httpcomponents</groupId><artifact…

【精选】java继承进阶——构造方法的访问特点 this、super使用

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

相机图像质量研究(7)常见问题总结:光学结构对成像的影响--镜片固化

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

Excel——分类汇总

1.一级分类汇总 Q&#xff1a;请根据各销售地区统计销售额总数。 第一步&#xff1a;排序&#xff0c;我们需要根据销售地区汇总数据&#xff0c;我们就要对【销售地区】的内容进行排序。点击【销售地区】列中任意一个单元格&#xff0c;选择【数据】——【排序】&#xff0c…

【快速上手QT】02-学会查看QT自带的手册QT助手

QT助手 为什么大家都说QT简单&#xff0c;第一点就是确实简单&#xff08;bushi&#xff09;。 我个人觉得最关键的点就是人家QT官方就给你准备好了文档&#xff0c;甚至还有专门的IDE——QtCreator&#xff0c;在QTCreator里面还有很多示例代码&#xff0c;只要你会C的语法以…

1.CVAT建项目步骤

文章目录 1. 创建project2. 创建task2.1. label 标签详解2.2.高级配置 Advanced configuration 3. 分配任务4. 注释者规范 CVAT的标注最小单位是Task&#xff0c;每个Task为一个标注任务。 1. 创建project 假设你并不熟悉cvat的标注流程&#xff0c;这里以图像2D目标检测为例进…

夜天之书 #95 GreptimeDB 社群观察报告

GreptimeDB 是格睿科技&#xff08;Greptime&#xff09;公司研发的一款开源时序数据库&#xff0c;其源代码[1]在 GitHub 平台公开发布。 https://github.com/GreptimeTeam/greptimedb 我从 2022 年开始知道有 GreptimeDB 这个项目。2023 年&#xff0c;我注意到他们的 Commun…

Hive 主要内容一览

Hive架构 用户接口&#xff1a;Client CLI&#xff08;command-line interface&#xff09;、JDBC/ODBC(jdbc访问hive) 元数据&#xff1a;Metastore 元数据包括&#xff1a;表名、表所属的数据库&#xff08;默认是default&#xff09;、表的拥有者、列/分区字段、表的类型&am…