parse 是使用 Python format() 的语法进行字符串数据解析的。
parse() 和 format() 是相互对立的两种形式。
模块仅提供了 parse()、search()、findall()、with_pattern() 作为开放的函数出口供使用者调用,
你可以使用这样的方式导入:
from parse import *
当你匹配字符串的时候将会变得很简单, 如下示例:
from parse import *
obj = parse("It's {}, I love it!", "It's spam, I love it!")
print(obj) # <Result ('spam',) {}>
print(obj[0]) # 'spam'
或者是使用某种指定的匹配模式进行匹配:
from parse import *
obj = search('Age: {:d}', 'Name: Rufus Age: 42 Color: red')
print(obj[0]) # 42
再或者你想查询一个匹配规则匹配某一个字符串的所有匹配对象:
from parse import *
obj = findall(">{}<", "<p>the <b>bold</b> text</p>")
for i in obj:
print(i[0])
# 注意输入的内容,我用双引号将依次输出的字符串括起来
# "the "、"bold"、" text"
如果你想使用一个匹配规则对象, 依次匹配多个字符串:
from parse import compile
obj = compile("It's {}, I love it!")
print(obj) # <Parser "It's {}, I love it!">
print(obj.parse("It's spam, I love it!")[0]) # spam
print(obj.parse("It's Medusa Blog, I love it!")[0]) # Medusa Blog
注意:
compile函数不会以from parse import *的方式导入, 因为 Python 内置的compile()函数会覆盖parse.compile()函数。
在默认行为中,
对字符串的大小写是不敏感的,
如果你想手动指定大小写是否敏感,
请使用 case_sensitive 参数进行声明:
from parse import *
obj = parse('SPAM', 'spam', case_sensitive=True)
print(obj) # None
格式和语法
在对规则进行定义的时候, 可以使用以下格式进行自定义输入规则下对应的字段映射以及特殊规则匹配属性:
{[fieldName]:[formatSpec]}
fieldName:字符名称, 必须符合 Python 标识符命名规则formatSpec:匹配的规则属性, 详见下文
如简单的示例:
from parse import *
obj = parse("The {} who say {}", "The knights who say Ni!")
print(obj.fixed) # ('knights', 'Ni!')
obj = parse("Bring out the holy {item}", "Bring out the holy hand grenade")
print(obj.named) # {'item': 'hand grenade'}
print(obj['item']) # hand grenade
print('item' in obj) # True
需要注意只有在存在明明字段的情况下 in 才会起作用。
from parse import *
obj = parse("Mmm, {food.type}, I love it!", "Mmm, spam, I love it!")
print(obj.named) # {'food.type': 'spam'}
print(obj['food.type']) # spam
obj = parse("My quest is {quest[name]}", "My quest is to seek the holy grail!")
print(obj) # <Result () {'quest': {'name': 'to seek the holy grail!'}}>
print(obj['quest']) # {'name': 'to seek the holy grail!'}
print(obj['quest']['name']) # to seek the holy grail!
匹配规则
通常情况下使用 {} 无模式的情况可以处理很复杂的字符匹配问题,
但是你也可以使用精确的匹配方法进行匹配,
也支持大多数的 format 的 Format Specification Mini-Language。
其中 parse() 和 format() 之间的区别是:
align运算符将导致从解析的值中删除空格(或指定的填充字符), 宽度不是强制的, 它只是表示可能有空白或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 | 字母 | str |
w | 字母、数字、下划线 | str |
W | 非字母、数字、下划线 | str |
s | 空白字符 | str |
S | 非空白字符 | str |
d | 有效整数 | int |
D | 非数字 | str |
n | 带有千位分隔符的数字 | int |
% | 百分比 | float |
f | 定点数 | float |
F | 小数 | Decimal |
e | 具有指数的浮点数, 例如 1.1e-10,NAN | float |
g | 通用数字格式(d、f、e) | float |
b | 二进制数 | int |
o | 八进制数 | int |
x | 十六进制数字 | int |
ti | ISO8601 格式的日期/时间, 例如 1972-01-20T10:21:36Z(T、Z 可选) | datetime |
te | RFC2822 电子邮件格式日期/时间, 例如 Mon, 20 Jan 1972 10:21:36 +1000 | datetime |
tg | Global (day/month) 格式日期/时间, 例如 20/1/1972 10:21:36 AM +1:00 | datetime |
ta | US (month/day) 格式的日期/时间, 例如 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 |
如果匹配规则没有匹配指定的字符串,
将会返回一个 None 类型的对象:
from parse import *
obj = parse('jump', 'go down')
print(obj) # None
特殊的对齐方式
个人觉得先用几个例子说明对齐方式的语法, 先不关注对齐的结果如何:
from parse import *
print(parse('with {:>} herring', 'with a herring')) # 右对齐
print(parse('with {:<} herring', 'with a herring')) # 左对齐
print(parse('with {:^} herring', 'with a herring')) # 居中对齐
当然你可以使用 {:>n} 来说明对其 n 个字符。
测试结果和结论:
from parse import *
print(parse('with {:>} herring', 'with a herring')) # <Result ('a',) {}>
print(parse('with {:<} herring', 'with a herring')) # None
print(parse('with {:^} herring', 'with a herring')) # None
print(parse('with{:^}herring', 'with a herring')) # <Result ('a',) {}>
print(parse('with{:^3}herring', 'with a herring')) # <Result ('a ',) {}>
- 右对齐, 匹配成功
- 左对齐, 没有匹配
- 居中对齐, 没有匹配
- 居中对齐, 匹配成功
- 居中对齐 3 个空白字符, 匹配成功
理解:匹配字符对于未指定对齐数目时, 左对齐需要对匹配字符匹配右侧空白字符;反之右对齐则不需要匹配空白字符。
注意看第五个结果 <Result ('a ',) {}>,
理想中的结果应该是 ' a '。理解:匹配中若右侧空格数满足匹配数,
则空格将会在右侧,
否则将使用左侧的作为填补。如下:
from parse import *
print(parse('with{:^3}herring', 'with a herring')) # <Result (' a',) {}>
print(parse('with{:^3}herring', 'with a herring')) # <Result ('a ',) {}>
print(parse('with{:^3}herring', 'with a herring')) # None
匹配结果类型
parse() 和 search() 的匹配结果可能是 None、Result 实例对象、Match 实例对象(evaluate_result=False)。
Result 实例拥有以下属性:
fixed从匹配结果中提取位置匿名字段的元组named冲匹配结果中提取命名字段的字典spans返回一个命名字段或元组下标作为键, 匹配字符的起始索引和结束索引的元组作为值的字典
Match 实例拥有以下属性:
evaluate_result()生成并返回此Match对象的Result实例
自定义类型转换
如果你想使用自定义匹配数据类型转换,
那么你可以将转换信息字典传递给 parse() 和 compile() 进行声明,
转换器将传递匹配的字段字符串,
返回的任何内容将在该字段的结果实例中被替换,
如果是具有相同标识符的内置类型,
自定义的将覆盖内置的。
from parse import *
def upper(string):
return string.upper()
obj = parse('{:name} world', 'hello world', dict(name=upper))
print(obj) # <Result ('HELLO',) {}>
如果类型转换器具有可选的 pattern 属性,
则将其用作正则表达式的模式以更好地匹配(而不是默认的):
from parse import *
def parse_number(text):
return int(text)
parse_number.pattern = r'\d+'
obj = parse('Answer: {number:Number}', 'Answer: 42', dict(Number=parse_number))
print(obj) # <Result () {'number': 42}>
obj = parse('Answer: {:Number}', 'Answer: Alice', dict(Number=parse_number))
print(obj) # None
你也可以使用 with_pattern(pattern) 修饰符(装饰器)将正则数据添加到类型转换器函数:
from parse import *
@with_pattern(r'\d+')
def parse_number(text):
return int(text)
obj = parse('Answer: {number:Number}', 'Answer: 42', dict(Number=parse_number))
print(obj) # <Result () {'number': 42}>
自定义一个完整的模型示例:
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,) {}>
如果使用类型转换器模式并使用正则表达式分组,
则应通过在 with_pattern() 修饰符中使用可选的 regex_group_count 参数来指示:
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) {}>
作者遇到的 BUG
在使用文档提示的安装操作后:
pip install parse
在 .py 文件进行导入后并不能生效,
进入 parse 包文件中和原本需要安装的包文件内容不一致,
哪怕下载后使用 setup.py install 都无法解决,
具体原因我也没有查到,
当然可以试试将 pip 源的 parse 文件下载下来做对比。
最终解决办法为,
在需要用到的项目中创建 lib 文件夹,
在 Github 地址中下载源码并解包放在 lib 文件夹中,
最终你可以使用这样的方式进行导入:
from lib.parse.parse import *
文件树展示: