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的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | b168ff54afdffaf913013fc7d9e8c01968fd6fce8b380e9ce8acc3e155ffb854 |
|
MD5 | 1ccc686f1735f57e127d77bd383f2c6d |
|
BLAKE2b-256 | e575f26c0925b1fbe38badaafcda7ed5ef82eabc785ff18aaab4563827d1f424 |