【译】parse 中文文档说明

2,871 阅读7分钟

MedusaSorcerer的博客


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!

匹配规则

通常情况下使用 {} 无模式的情况可以处理很复杂的字符匹配问题, 但是你也可以使用精确的匹配方法进行匹配, 也支持大多数的 formatFormat Specification Mini-Language

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

  • align 运算符将导致从解析的值中删除空格(或指定的填充字符), 宽度不是强制的, 它只是表示可能有空白或 0 要去除
  • 数值分析将自动处理 0b0o0x 前缀。也就是说, # 格式字符由 dbox 格式自动处理。对于 d, 任何一个都可以接受, 但是对于其他的必须有正确的前缀
  • 数字符号是自动处理的
  • 如果使用 n 类型, 则自动处理千位分隔符
  • 支持的类型与 format() 类型略有不同, 有些 format() 类型直接出现在下面:dn%febox。此外, 还提供了一些正则表达式字符组类型 Dwwss
  • eg 类型不区分大小写, 因此不需要 EG 类型, 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,NANfloat
g通用数字格式(dfe)float
b二进制数int
o八进制数int
x十六进制数字int
tiISO8601 格式的日期/时间, 例如 1972-01-20T10:21:36ZTZ 可选)datetime
teRFC2822 电子邮件格式日期/时间, 例如 Mon, 20 Jan 1972 10:21:36 +1000datetime
tgGlobal (day/month) 格式日期/时间, 例如 20/1/1972 10:21:36 AM +1:00datetime
taUS (month/day) 格式的日期/时间, 例如 1/20/1972 10:21:36 PM +10:30datetime
tcctime() 格式日期/时间, 例如 Sun Sep 16 01:03:52 1973datetime
thHTTP 日志格式日期/时间, 例如 21/Nov/2011:00:07:11 +0000datetime
tsLinux 系统日志格式日期/时间, 例如 Nov 9 03:37:44datetime
tt时间, 例如 10:21:36 PM -5:30time

如果匹配规则没有匹配指定的字符串, 将会返回一个 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  ',) {}>
  1. 右对齐, 匹配成功
  2. 左对齐, 没有匹配
  3. 居中对齐, 没有匹配
  4. 居中对齐, 匹配成功
  5. 居中对齐 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() 的匹配结果可能是 NoneResult 实例对象、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 *

文件树展示: