跳转到主要内容

parse() 是 format() 的反义词

项目描述

安装

pip install parse

用法

使用基于Python format()语法的规范解析字符串。

parse()format() 的反义词

当使用 import * 时,该模块配置为仅导出 parse()search()findall()with_pattern()

>>> from parse import *

然后就可以简单地解析一个字符串

>>> parse("It's {}, I love it!", "It's spam, I love it!")
<Result ('spam',) {}>
>>> _[0]
'spam'

或者搜索一个字符串以查找某些模式

>>> search('Age: {:d}\n', 'Name: Rufus\nAge: 42\nColor: red\n')
<Result (42,) {}>

或者在字符串中查找某些模式的所有出现

>>> ''.join(r[0] for r in findall(">{}<", "<p>the <b>bold</b> text</p>"))
'the bold text'

如果您打算使用相同的模式匹配多个字符串,您可以一次性编译它

>>> from parse import compile
>>> p = compile("It's {}, I love it!")
>>> print(p)
<Parser "It's {}, I love it!">
>>> p.parse("It's spam, I love it!")
<Result ('spam',) {}>

(“compile” 在 import * 使用中未导出,因为它会覆盖内置的 compile() 函数)

默认行为是不区分大小写地匹配字符串。你可以通过指定 case_sensitive=True 来进行大小写匹配。

>>> parse('SPAM', 'spam', case_sensitive=True) is None
True

格式语法

支持基本版本的 格式字符串语法,包括匿名(固定位置)、命名和格式化字段。

{[field name]:[format spec]}

字段名必须是有效的 Python 标识符,包括点分隔的名称;元素索引表示字典(以下示例中说明)。

不支持编号字段:解析的结果将按解析顺序包含解析的字段。

字段转换为除字符串以外的类型是基于格式规范中的类型,这与 format() 的行为相一致。没有类似于 format() 中的“!”字段转换。

一些简单的 parse() 格式字符串示例

>>> parse("Bring me a {}", "Bring me a shrubbery")
<Result ('shrubbery',) {}>
>>> r = parse("The {} who {} {}", "The knights who say Ni!")
>>> print(r)
<Result ('knights', 'say', 'Ni!') {}>
>>> print(r.fixed)
('knights', 'say', 'Ni!')
>>> print(r[0])
knights
>>> print(r[1:])
('say', 'Ni!')
>>> r = parse("Bring out the holy {item}", "Bring out the holy hand grenade")
>>> print(r)
<Result () {'item': 'hand grenade'}>
>>> print(r.named)
{'item': 'hand grenade'}
>>> print(r['item'])
hand grenade
>>> 'item' in r
True

注意,in 只在存在命名字段时才有效。

点分隔的名称和索引是可能的,但有一些限制。只支持单词标识符(即不允许数字索引),并且应用程序必须对结果进行额外的解释。

>>> r = parse("Mmm, {food.type}, I love it!", "Mmm, spam, I love it!")
>>> print(r)
<Result () {'food.type': 'spam'}>
>>> print(r.named)
{'food.type': 'spam'}
>>> print(r['food.type'])
spam
>>> r = parse("My quest is {quest[name]}", "My quest is to seek the holy grail!")
>>> print(r)
<Result () {'quest': {'name': 'to seek the holy grail!'}}>
>>> print(r['quest'])
{'name': 'to seek the holy grail!'}
>>> print(r['quest']['name'])
to seek the holy grail!

如果你要匹配的文本中有花括号,你可以在格式字符串中包含双花括号 {{}} 来匹配这些花括号,就像 format() 一样。

格式规范

通常,在不使用更复杂的格式规范的情况下,使用直接的无格式 {} 就足够了。

大部分 format()格式规范迷你语言 都得到了支持。

[[填充]对齐][符号][0][宽度][.精度][类型]

parse()format() 之间的区别是:

  • 对齐运算符会导致从解析值中移除空格(或指定的填充字符)。宽度不是强制性的;它只是表示可能存在空格或“0”需要移除。

  • 数字解析会自动处理“0b”、“0o”或“0x”前缀。即,“#”格式字符会自动由 d、b、o 和 x 格式处理。对于“d”,任何前缀都可以接受,但对于其他格式,如果存在,则必须具有正确的前缀。

  • 数字符号会自动处理。可以提供符号指定符,但它没有效果。

  • 如果使用“n”类型,则会自动处理千位分隔符。

  • 支持的类型与 format() 类型略有不同。一些 format() 类型直接映射过来:“d”、“n”、“%”、“f”、“e”、“b”、“o”和“x”。此外,一些正则表达式字符组类型“D”、“w”、“W”、“s”和“S”也是可用的。

  • “e”和“g”类型不区分大小写,因此不需要“E”或“G”类型。“e”类型处理 Fortran 格式的数字(小数点前没有前导 0)。

类型

匹配的字符

输出

l

字母(ASCII)

str

w

字母、数字和下划线

str

W

非字母、数字和下划线

str

s

空白字符

str

S

非空白字符

str

d

数字(实际上是整数)

int

D

非数字

str

n

带千位分隔符的数字(, 或 .)

int

%

百分比(转换为值/100.0)

float

f

定点数

float

F

十进制数

Decimal

e

带有指数的浮点数,例如 1.1e-10,NAN(所有大小写不敏感)

float

g

通用数字格式(d、f 或 e)

float

b

二进制数

int

o

八进制数

int

x

十六进制数(大小写不敏感)

int

ti

ISO 8601 格式的日期/时间,例如 1972-01-20T10:21:36Z(“T”和“Z”可选)

datetime

te

RFC2822 电子邮件格式日期/时间,例如 Mon, 20 Jan 1972 10:21:36 +1000

datetime

tg

全球(日/月)格式日期/时间,例如 20/1/1972 10:21:36 AM +1:00

datetime

ta

美国(月/日)格式日期/时间,例如 1/20/1972 10:21:36 PM +10:30

datetime

tc

ctime()日期/时间格式,例如:Sun Sep 16 01:03:52 1973

datetime

th

HTTP日志格式日期/时间,例如:21/Nov/2011:00:07:11 +0000

datetime

ts

Linux系统日志格式日期/时间,例如:Nov 9 03:37:44

datetime

tt

时间,例如:10:21:36 PM -5:30

time

类型也可以是日期时间格式字符串,遵循1989年C标准格式代码,例如%Y-%m-%d。根据格式字符串中包含的指令,解析输出可能是datetime.datetimedatetime.timedatetime.date的一个实例。

>>> parse("{:%Y-%m-%d %H:%M:%S}", "2023-11-23 12:56:47")
<Result (datetime.datetime(2023, 11, 23, 12, 56, 47),) {}>
>>> parse("{:%H:%M}", "10:26")
<Result (datetime.time(10, 26),) {}>
>>> parse("{:%Y/%m/%d}", "2023/11/25")
<Result (datetime.date(2023, 11, 25),) {}>

以下是一些类型解析的示例,如果类型不匹配则返回None

>>> parse('Our {:d} {:w} are...', 'Our 3 weapons are...')
<Result (3, 'weapons') {}>
>>> parse('Our {:d} {:w} are...', 'Our three weapons are...')
>>> parse('Meet at {:tg}', 'Meet at 1/2/2011 11:00 PM')
<Result (datetime.datetime(2011, 2, 1, 23, 0),) {}>

并且与对齐有关的问题

>>> parse('with {:>} herring', 'with     a herring')
<Result ('a',) {}>
>>> parse('spam {:^} spam', 'spam    lovely     spam')
<Result ('lovely',) {}>

请注意,“居中”对齐并不检查值是否居中,它只是删除前导和尾随空白。

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

>>> parse('{:.2}{:.2}', 'look')           # specifying precision
<Result ('lo', 'ok') {}>
>>> parse('{:4}{:4}', 'look at that')     # specifying width
<Result ('look', 'at that') {}>
>>> parse('{:4}{:.4}', 'look at that')    # specifying both
<Result ('look at ', 'that') {}>
>>> parse('{:2d}{:2d}', '0440')           # parsing two contiguous numbers
<Result (4, 40) {}>

有关特殊日期和时间类型的说明

  • 时间部分是可选的(包括ISO 8601,从“T”开始)。始终将返回完整的日期时间对象;时间将设置为00:00:00。您还可以指定不包含秒的时间。

  • 如果输入中存在秒数,则将解析分数以给出微秒。

  • 除ISO 8601外,日期和月份数字可以是0填充的。

  • tg和ta格式的日期分隔符可以是“-”或“/”。

  • 在ta和tg格式中,可以使用名称月份(缩写或全称)代替数字月份。

  • 根据RFC 2822,电子邮件格式可以省略日期(和逗号)、秒数,但不能省略其他内容。

  • 大于12小时的小时数将被愉快地接受。

  • AM/PM是可选的,如果找到PM,则将向日期时间对象的时数添加12小时 - 即使小时数大于12(为了保持一致)。

  • 在ISO 8601中,“Z”(UTC)时区部分可以是数字偏移量

  • 时区指定为“+HH:MM”或“-HH:MM”。小时数可以是1或2位数字(0填充是可接受的)。此外,“:”是可选的。

  • 时区在电子邮件格式之外都是可选的(默认为UTC)。

  • 尚未处理命名时区。

注意:尝试在单个解析()中匹配过多的日期时间字段,当前将导致资源分配问题。在这种情况下将引发TooManyFields异常。当前的限制约为15。希望有一天会取消这个限制。

结果和匹配对象

parse()search()操作的结果要么是None(没有匹配),要么是Result实例,或者如果evaluate_result为False,则是Match实例。

Result实例有三个属性

fixed

从输入中提取的固定位置、匿名字段的元组。

named

从输入中提取的命名字段的字典。

spans

将名称和固定位置索引映射到在输入中匹配发生的位置的2元组切片范围的字典。范围不包括任何被删除的填充(对齐或宽度)。

Match实例有一个方法

evaluate_result()

为此Match对象生成并返回一个Result实例。

自定义类型转换

如果您希望自动将匹配的字段转换为您的类型,您可以将类型转换信息字典传递给parse()compile()

转换器将传递匹配的字段字符串。它返回的内容将被替换到该字段的结果实例中。

如果您提供了一个与内置类型具有相同标识符的自定义类型转换,则可以覆盖内置类型。

>>> def shouty(string):
...    return string.upper()
...
>>> parse('{:shouty} world', 'hello world', {"shouty": shouty})
<Result ('HELLO',) {}>

如果类型转换器具有可选的模式属性,它将被用作正则表达式以进行更好的模式匹配(而不是默认模式)。

>>> def parse_number(text):
...    return int(text)
>>> parse_number.pattern = r'\d+'
>>> parse('Answer: {number:Number}', 'Answer: 42', {"Number": parse_number})
<Result () {'number': 42}>
>>> _ = parse('Answer: {:Number}', 'Answer: Alice', {"Number": parse_number})
>>> assert _ is None, "MISMATCH"

您还可以使用with_pattern(pattern)装饰器将此信息添加到类型转换器函数中。

>>> from parse import with_pattern
>>> @with_pattern(r'\d+')
... def parse_number(text):
...    return int(text)
>>> parse('Answer: {number:Number}', 'Answer: 42', {"Number": parse_number})
<Result () {'number': 42}>

一个自定义类型的更完整示例可能如下

>>> 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()]

如果类型转换器的模式使用正则表达式分组(带有括号),您应该通过在with_pattern()装饰器中使用可选的regex_group_count参数来指出这一点。

>>> @with_pattern(r'((\d+))', regex_group_count=2)
... def parse_number2(text):
...    return int(text)
>>> parse('Answer: {:Number2} {:Number2}', 'Answer: 42 43', {"Number2": parse_number2})
<Result (42, 43) {}>

否则,这可能会导致解析问题,涉及无名称/固定参数。

潜在问题

parse()始终匹配满足解析模式的最短文本(从左到右),因此例如

>>> pattern = '{dir1}/{dir2}'
>>> data = 'root/parent/subdir'
>>> sorted(parse(pattern, data).named.items())
[('dir1', 'root'), ('dir2', 'parent/subdir')]

因此,尽管{‘dir1’: ‘root/parent’, ‘dir2’: ‘subdir’}也符合模式,但实际上匹配的是dir1的最短成功匹配。

开发者

想要为parse做出贡献?将仓库Fork到您的GitHub账户,并创建一个pull-request。

git clone git@github.com:r1chardj0n3s/parse.git
git remote rename origin upstream
git remote add origin git@github.com:YOURUSERNAME/parse.git
git checkout -b myfeature

要本地运行测试

python -m venv .venv
source .venv/bin/activate
pip install -r tests/requirements.txt
pip install -e .
pytest

变更日志

  • 1.20.2 模板字段名称现在可以包含短横线字符,即连字符(chr(0x2d))。

  • 1.20.1 %f指令接受1-6位数字,类似于strptime(感谢@bbertincourt)。

  • 1.20.0 增加了strptime代码的支持(感谢@bendichter)。

  • 1.19.1 增加了数字格式中的符号说明符的支持(感谢@anntzer)。

  • 1.19.0 增加了固定结果的切片访问(感谢@jonathangjertsen)。还纠正了全文全文行的匹配问题(感谢@giladreti)。修复了使用数字字段编号和类型的问题。

  • 1.18.0 修复了1.16.0中引入的int解析错误(感谢@maxxk)。

  • 1.17.0 使左对齐和居中对齐搜索消耗到下一个空格。

  • 1.16.0 使编译的解析对象可序列化(感谢@martinResearch)。

  • 1.15.0 对非十进制数字的解析进行了多项修复(感谢@vladikcomper)。

  • 1.14.0 更广泛地接受Fortran数字格式(感谢@purpleskyfall)。

  • 1.13.1 项目元数据更正。

  • 1.13.0 处理没有小数点前零的Fortran格式数字(感谢@purpleskyfall)。处理FixedTzOffset与其他类型对象的比较。

  • 1.12.1 实际使用compile中的case_sensitive参数(感谢@jacquev6)。

  • 1.12.0 在找到开括号时不假设有闭括号(感谢@mattsep)。

  • 1.11.1 回滚docstring中的unicode字符,因为它破坏了Bamboo构建(?!)

  • 1.11.0 实现了Result实例的__contains__

  • 1.10.0 引入了一个“letters”匹配器,因为“w”也会匹配数字。

  • 1.9.1 修复了正则表达式字符串中的反斜杠的弃用警告(感谢Mickael Schoentgen)。还修复了一些文档格式问题。

  • 1.9.0 现在在解析数字和字符串时尊重精度和宽度说明符,允许解析固定宽度的连接元素(感谢Julia Signell)。

  • 1.8.4 根据打包者的请求添加了LICENSE文件。更正了AM/PM的处理,以遵循最普遍的解释。更正了类似二进制前缀的十六进制解析。添加了以“F”将数字解析到Decimal的能力。添加了解析大小写敏感的能力(感谢John Vandenberg)。

  • 1.8.3 在with_pattern()装饰器中添加了regex_group_count,以支持包含括号/括号的用户定义类型(感谢Jens Engel)。

  • 1.8.2 为包括花括号在格式字符串中添加了文档。

  • 1.8.1 确保纯十六进制数字不被匹配。

  • 1.8.0 支持手动控制结果评估(感谢Timo Furrer)。

  • 1.7.0 解析字典字段(感谢Mark Visser)并适配Python 3.5+中的100多个正则表达式组(感谢David King)。

  • 1.6.6 解析Linux系统日志日期(感谢Alex Cowan)。

  • 1.6.5 处理浮点格式中的精度(感谢 Levi Kilcher)

  • 1.6.4 处理解析字符串中的管道“|”字符(感谢 Martijn Pieters)

  • 1.6.3 处理命名字段的重复实例,修复 PM 时间溢出错误

  • 1.6.2 修复日志记录使用本地记录器,而不是根记录器(感谢 Necku)

  • 1.6.1 在匹配 ISO 日期时间和时区方面更具灵活性,修复没有“:”的时区错误,并改进文档

  • 1.6.0 在用户定义类型中添加对可选 pattern 属性的支持(感谢 Jens Engel)

  • 1.5.3 修复处理问号的问题

  • 1.5.2 修复使用点名称的错误类型转换(感谢 Sebastian Thiel)

  • 1.5.1 实现处理命名日期时间字段

  • 1.5 添加对点字段名称的处理(感谢 Sebastian Thiel)

  • 1.4.1 修复 int 转换中“0”的解析(感谢 James Rowe)

  • 1.4 在 Result 中添加 __getitem__ 便利访问

  • 1.3.3 修复 Python 2.5 setup.py 的问题。

  • 1.3.2 修复 Python 3.2 setup.py 的问题。

  • 1.3.1 修复一些 Python 3.2 兼容性问题。

  • 1.3 添加 search() 和 findall();从 import * 导出时移除 compile(),因为它会覆盖内置函数。

  • 1.2 添加自定义和重写类型转换的能力;进行一些清理

  • 1.1.9 为了使事情更简单,自动处理数字符号;针对边缘情况输入进行显著的健壮性改进。

  • 1.1.8 允许“d”字段具有“0x”等数字基前缀;在压力测试解析器后修复一些字段类型交互;实现“%”类型。

  • 1.1.7 Python 3 兼容性调整(支持 2.5 到 2.7 和 3.2)。

  • 1.1.6 添加“e”和“g”字段类型;删除冗余的“h”和“X”;删除显式“#”的需求。

  • 1.1.5 在更多地方接受文本日期;Result 现在保留匹配的跨度位置。

  • 1.1.4 对某些 int 类型转换进行修复;实现“=”对齐;添加支持多种格式的日期/时间解析。

  • 1.1.3 类型转换根据指定的字段类型自动进行。还添加了“f”和“n”类型。

  • 1.1.2 重构,添加 compile() 和有限 from parse import * 导出

  • 1.1.1 改进文档

  • 1.1.0 实现了更多 格式规范迷你语言 的功能,并删除了混合固定位置和命名字段的限制

  • 1.0.0 首次发布

本代码版权所有 2012-2021 Richard Jones <richard@python.org> 请参阅源文件末尾的使用许可。

项目详情


下载文件

下载您平台上的文件。如果您不确定选择哪个,请了解更多关于 安装软件包 的信息。

源代码分发

parse-1.20.2.tar.gz (29.4 kB 查看哈希值)

上传时间 源码

构建的发行版

parse-1.20.2-py2.py3-none-any.whl (20.1 kB 查看哈希值)

上传时间 Python 2 Python 3

由以下机构支持