使用 pyparsing.srange 实现大字符范围

问题描述 投票:0回答:1

我的代码中有一些这样的结构:

from pyparsing import Char, srange

_any_char = Char(srange('[\x00-\U0010FFFF]'))

范围的生成非常慢,即使在相对不错的机器上使用 Python 3.12,也需要 6-8 秒(哈?)。

为什么会发生这种情况?我应该用什么来替换它们?

python performance pyparsing
1个回答
0
投票

这就是

srange()
函数 的样子(已删除文档字符串):

_charRange = Group(_singleChar + Suppress("-") + _singleChar)
_reBracketExpr = (
    Literal("[")
    + Opt("^").set_results_name("negate")
    + Group(OneOrMore(_charRange | _singleChar)).set_results_name("body")
    + Literal("]")
)

def srange(s: str) -> str:
    _expanded = (
        lambda p: p
        if not isinstance(p, ParseResults)
        else "".join(chr(c) for c in range(ord(p[0]), ord(p[1]) + 1))
    )
    try:
        return "".join(_expanded(part) for part in _reBracketExpr.parse_string(s).body)
    except Exception as e:
        return ""

正如你(或我)所看到的,

pyparsing
正在使用
_reBracketExpr
解析参数,它本身就是一个
pyparsing
表达式,然后将结果传递给
_expanded
。如果
_expanded
p
(范围),
ParseResult
将创建一个新字符串,否则返回
p
不变(单个字符)。最后,所有这些字符串都连接起来,创建一个更大的字符串。

在您(或我)的情况下,每个

Char(srange())
创建一个
0x10FFFF
-
0x80
+
1
= ~
1.1m
字符串。幸运的是,由于只有一个元素,因此会重用该字符串而不是连接来创建最终结果。您(或我)可以通过运行来确认这一点:

print(''.join((a := 'c' * 100000) for e in [0]) is a)  # True, at least on Python 3.12

至于替代方案,我切换到

Regex
(在引擎盖下使用本机
re
):

_any_char = Regex(r'[\x80-\U0010FFFF]')  # With or without r are both fine.

没有创建巨大的字符串,希望与正常的字符串一样高效

Char

© www.soinside.com 2019 - 2024. All rights reserved.