【问题标题】:Regex/matching engine with completely overlapping results and "cursor" manipulation具有完全重叠结果和“光标”操作的正则表达式/匹配引擎
【发布时间】:2017-02-10 19:41:07
【问题描述】:

我正在寻找类似于正则表达式引擎的东西,但它允许完全重叠的结果,并允许在引擎返回匹配项时操作内部“光标”。

正常的正则表达式方式:

假设您有一个带有各种“替代项”的普通正则表达式:item1|item2|item3,并且您使用findallfinditer 来获取所有匹配项。在输入字符串中的某个位置,引擎可能会匹配这些备选方案中的任何一个。一旦找到,光标在匹配结束后立即前进到索引,并继续寻找任何替代方案。即使其中两个或多个备选方案可能与初始光标位置处的字符串匹配,也只会返回一个:

import re
input_string = 'foobar'
compiled = re.compile('foobar|foo|bar')
compiled.findall(input_string)

# 'foobar'  

我想要什么(1):

我希望将它们全部归还。像这样:

import muchneededregexthing
input_string = 'foobar'
compiled = muchneededregexthing.compile('foobar|foo|bar')
searcher = compiled.create_searcher(input_string)
while not searcher.reached_end():
    match = searcher.search() # increments searcher's internal cursor 
                              # to after the end of the match
    print(match.string, match.span())

# foobar (0,6)
# foo (0,3)
# bar (3,6)

我还想要什么(2):

我希望能够修改搜索器的光标,以便我可以根据运行时发生的情况来操作结果('foobar' 匹配,'foo' 和 'bar' 分别无关紧要)。

import muchneededregexthing
input_string = 'foobarkitten'
compiled = muchneededregexthing.compile('foobar|foo|bar|kitten')
searcher = compiled.create_searcher(input_string)
while not searcher.reached_end():
    match = searcher.search() # increments searcher's internal cursor 
                              # to after the end of the match
    print(match.string, match.span())
    if match.string == 'foobar':
        searcher.advance_cursor(match.end())

# foobar (0,6)
# kitten (6,12)

我不能使用的东西(很可能):

  • str.find:我需要正则表达式的东西用于标记(markdown/wiki/etc)解析器。在任何时候,都可能需要寻找许多不同的元素。使用 str.find 我需要使用标记支持的每个元素搜索整个文档。该语言过于复杂,无法将文档细分为“标题块”、“段落块”等块。

  • re: 除非 re 神奇地支持我需要的东西,否则我不能使用它,原因如下:正则表达式是有限的,你无法匹配任何你喜欢的东西。然而,它们的属性对于像我这样的情况很有用。我计划分两个阶段进行匹配:正则表达式为元素提供可能的匹配。然后就可能匹配是否为匹配咨询智能函数/方法。如果是这样,很好,前进光标。如果没有,请搞砸foobar,给foobar 一个机会。

非常欢迎提出想法。我相当肯定我需要上述功能。到目前为止,我最好的想法是编写我自己的 muchneededregexthing 模块,支持 C/C++ 中的大多数正则表达式语法,所以不要担心我会因为牵强而忽视你的想法。

编辑 1:请求标记示例

标记元素,因此需要匹配的标记,是通过插件定义和引入的。因此,框架本身不包含任何标记。我可以简单地将插件的令牌与正则表达式匹配并完成它,但我想至少探索选项并尝试允许比正则表达式能够支持的更大范围的标记令牌。例如,如果string:number 的关系是数字应该是字符串的某种数字表示,他们将如何匹配? a:0 是一个有效的令牌,但 a:1 不是。但是b:1 是,bc:28 (1*26 + 2*1) 也是。

对于这个例子,插件可以提供一个正则表达式,例如([a-z]{1,5})([0-9]{1,5})。然后,该算法会将匹配传递给一个特殊函数,该函数计算第一组的数值并将其与第二组的值进行比较。如果这些值匹配,则插件将处理文档的这一部分。如果不是,则返回并尝试让另一个插件处理文档中的此索引。

【问题讨论】:

  • 重叠匹配的默认行为是在每次匹配后将光标前进一个位置。如果不允许前进至少一个位置,则进入无限循环。正则表达式是固定的。字符串 fooooo 上的示例 (?=(o+|foo)) 找到 5 个匹配项。此外,单个字符一次存在于一个位置,因此只有正则表达式可以是可变的。 IE。 item_X 只能是 _X 而不能是 Y。但是,正则表达式是固定在一个状态中的,所以它永远不会改变。因此,位置的推进。
  • 您可以使用多个正则表达式来扫描单个位置。但是您必须通过设置正则表达式并在字符串中设置位置来控制它。这是一个单一的动作。
  • 您能举一个降价的例子吗?它可能可以通过正则表达式或递归解析器来解决。
  • 正如 Jamie Zawinski 的名言,有些人在遇到问题时会想“我知道,我会使用正则表达式”。现在他们有两个问题。 任何类型的标记解析都应该使用真正的解析器来完成。相信我——当你尝试实现一个不希望转换标记的逐字部分时,你就会意识到它的真相。
  • @btilly 我同意,因此框架(它是一个框架)有一个“真实的”(递归下降)解析器。类似正则表达式的部分纯粹是为了让人们轻松编写插件(包含自定义标记元素)。

标签: python regex algorithm parsing markup


【解决方案1】:

如果您保证不会有两个不同的模式在相同的位置以相同的长度匹配,那么您可以使用纯 Python 轻松实现这一点。

如果pattern 是已编译的正则表达式,则pattern.search(str, start) 将查找从start 开始的匹配项,而pattern.search(str, start, length) 将查找从start 开始且长度最多为length 的匹配项。

所以搜索一下。前进到匹配的位置。检查是否需要。如果没有,那么决定扔掉它。使用相同的模式和较短的长度在同一点进行另一次搜索。如果不匹配或匹配得更远,请推进光标。

【讨论】:

  • 公平点,但不能保证这些模式都有不同的长度。此外,这会施加顺序限制(最长的模式优先),而我宁愿根据元素的优先级对模式进行排序(标题的优先级高于段落,因此标题模式将是第一个)。
【解决方案2】:

对于那些寻求这个问题答案的未来流浪者:我最近遇到了hyperscan,它正是我想要的。它在 C 中,但有一个 Python port 用于它(我没有测试端口,但原生 C 版本运行良好)。

虽然大多数正则表达式引擎将仅匹配输入字符串中给定偏移量的一个子模式,但 Hyperscan 将报告与每个偏移量匹配的所有子模式。另见hyperscan semantics

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多