跳转到主要内容

parse()是format()的反义词

项目描述

这是一个基于parse库的分支,增加了解析日期时间格式的能力。

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

parse()format()的反义词

当使用import \*时,该模块只导出parse()search()findall()with_pattern()

>>> from fparse 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 fparse 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()函数,因此不导出compile用于import \*的使用)

默认行为是不区分大小写地匹配字符串。您可以通过指定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

%

百分比(转换为value/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 或 %y 的类型都将被解析并输出为 datetime.datetime。例如,

>>> parse('Meet at {:%Y-%m-%d}', 'Meet at 2023-05-15')
<Result (datetime.datetime(2023, 15, 5),) {}>

一些使用 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,则将向日期时间对象的 hours 量添加 12 小时 - 即使小时数大于 12(为了保持一致性。)

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

  • 时区指定为“+HH:MM”或“-HH:MM”。小时可以是一位或两位数字(0 补齐是可以的。)此外,“:”是可选的。

  • 除了电子邮件格式外,时区是可选的(它默认为 UTC。)

  • 尚未处理命名时区。

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

结果和匹配对象

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

Result 实例有三个属性

fixed

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

named

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

spans

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

Match 实例有一个方法

evaluate_result()

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

自定义类型转换

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

转换器将传入匹配的字段字符串。它返回的任何内容都将替换为该字段的结果实例。

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

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

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

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

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

>>> from fparse import with_pattern
>>> @with_pattern(r'\d+')
... def parse_number(text):
...    return int(text)
>>> parse('Answer: {number:Number}', 'Answer: 42', dict(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', dict(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的最短成功匹配。


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

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

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

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

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

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

  • 1.13.1 项目元数据修正。

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

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

  • 1.12.0 当发现开括号时,不要假设有闭括号(感谢 @mattsep)。

  • 1.11.1 撤销在文档字符串中使用的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 添加了搜索()和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> 使用许可请参阅源文件末尾。

项目详情


下载文件

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

源代码分发

fparse-1.20.1.tar.gz (28.2 kB 查看哈希值)

上传时间 源代码

由以下支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面