是否需要更声明式的正则表达式写法? :)

14 投票
4 回答
675 浏览
提问于 2025-04-16 02:31

我正在尝试创建一个Python函数,这个函数可以接受用简单英语描述的正则表达式,然后返回这个正则表达式给调用者。

目前我在考虑用YAML格式来描述这个正则表达式。这样,我们可以把描述存储为一个原始字符串变量,然后把这个变量传递给另一个函数,最后这个函数的输出再传给're'模块。下面是一个相对简单的例子:

# a(b|c)d+e*
re1 = """
- literal: 'a'
- one_of: 'b,c'
- one_or_more_of: 'd'
- zero_or_more_of: 'e'
"""
myre = re.compile(getRegex(re1))
myre.search(...)

等等。

有没有人觉得这样的东西会更有用呢?你知道有没有现成的包可以做到这一点吗?你觉得这种方法有什么局限性?有没有人认为,把声明式字符串放在代码里会让它更容易维护呢?

4 个回答

6

这其实和词法分析器/解析器的工作方式很相似(甚至可以说是一样)。如果你有一个明确的语法规则,那么你可能可以比较轻松地写出一个解析器。例如,你可以写出这样的代码:

<expression> :: == <rule> | <rule> <expression> | <rule> " followed by " <expression>
<rule>       :: == <val> | <qty> <val>
<qty>        :: == "literal" | "one" | "one of" | "one or more of" | "zero or more of"
<val>        :: == "a" | "b" | "c" | "d" | ... | "Z" | 

这并不是一个完美的描述。如果想了解更多信息,可以看看这个正则表达式的BNF。然后你可以了解一下词法分析解析这个表达式。

如果你这样做,可能会更接近于自然语言或英文版本的正则表达式。


我觉得这样的工具会很有用,但正如之前所说的,主要是对初学者有帮助。这个方法的主要限制在于你需要写很多代码来把语言翻译成正则表达式(或者反过来)。另一方面,我认为一个双向翻译工具会更理想,也会更常用。能够把正则表达式转成英文,可能更有助于发现错误。

当然,学习正则表达式并不会花太多时间,因为它的语法通常比较简洁,而且大部分含义也很容易理解,至少如果你把 | 或者 || 当作你语言中的OR,并且把 * 理解为乘以0到N,+ 理解为加上0到N。

不过有时候,我倒是希望能直接输入“找到一个或多个'a'后面跟着三个数字,或者'b'再跟着'c'”。

7

请看看pyparsing。你提到的很多正则表达式(RE)的问题,正是我写这个工具包的原因。

以下是来自O'Reilly电子书章节“pyparsing有什么特别之处?”的一些具体特点。

3

对于那些想写出容易理解和维护的正则表达式的开发者,我在想,这种方法是否能提供一些re.VERBOSE已经没有的东西。

对于初学者来说,你的想法可能会有些吸引力。不过,在你深入这个方向之前,建议你先试着设计一下你的声明式语法,看看在处理更复杂的正则表达式时,比如使用捕获组、锚点、前瞻断言等等,会是什么样子。一个挑战是,你可能会得到一种声明式语法,它和正则表达式语言本身一样难以记住。

你也可以考虑其他表达方式。例如,我想到的第一个想法是用一些简短、容易记住的函数来表达正则表达式。例如:

from refunc import *

pattern = Compile(
    'a',
    Capture(
        Choices('b', 'c'),
        N_of( 'd', 1, Infin() ),
        N_of( 'e', 0, Infin() ),
    ),
    Look_ahead('foo'),
)

但是当我看到这个在实际应用中时,我觉得这很麻烦。正则表达式有很多直观的部分,比如+表示“一个或多个”。一种选择是混合使用,让用户可以将那些已经简单的正则部分和更复杂的部分用函数结合起来。

pattern = Compile(
    'a',
    Capture(
        '[bc]',
        'd+',
        'e*',
    ),
    Look_ahead('foo'),
)

我还想补充一点,根据我的经验,正则表达式主要是关于学习一种思维方式。熟悉语法其实是比较简单的部分。

撰写回答