【问题标题】:RegEx - Order of OR'd values in capture group changes resultsRegEx - 捕获组更改结果中的 OR'd 值的顺序
【发布时间】:2017-02-24 06:41:21
【问题描述】:

Visual Studio/XPath/RegEx:

给定表达式:

(?<TheObject>(Car|Car Blue)) +(?<OldState>.+) +---> +(?<NewState>.+)

给定搜索字符串:

Car Blue Flying ---> Crashed

我预计:

TheObject = "Car Blue"
OldState = "Flying"
NewState = "Crashed"

我得到了什么:

TheObject = "Car"
OldState = "Blue Flying"
NewState = "Crashed"

鉴于新的正则表达式:

(?<TheObject>(Car Blue|Car)) +(?<OldState>.+) +---> +(?<NewState>.+)

结果是(我想要的):

TheObject = "Car Blue"
OldState = "Flying"
NewState = "Crashed"

我从概念上了解幕后发生的事情; RegEx 将它在 OR'd 列表中找到的第一个(从左到右)匹配放入 &lt;TheObject&gt; 组,然后继续。

OR'd 列表是在运行时构建的,不能保证将“Car”或“Car Blue”添加到 &lt;TheObject&gt; 组中的 OR'd 列表中的顺序。 (这是大大简化的 OR'd 列表)

我可以通过将 OR 列表从最长到最短排序来强制它,但是,我正在寻找更优雅的东西。

有没有办法让&lt;TheObject&gt; 组捕获它可以在 OR'd 列表中找到的最大而不是它找到的第一个? (不用我担心订单)

谢谢,

【问题讨论】:

  • 非常有趣的问题。我认为您除了按长度排序之外别无选择(反向字母可能更快/更容易?)或者可能像&lt;Car Blue&gt; Flying ---&gt; Crashed或更确切地说&lt;Noun&gt; &lt;Action&gt; ----&gt; &lt;Result&gt;那样分隔/标记字符串但是是的,您的正则表达式基本上是在说“任何与当前需求、空间以及之后的任何东西相匹配的东西。它做出更长选择的唯一方法是它是否需要CarsCarpool。或者如果你可以对任何作为占位符的飞行设置一些限制,就像它不能包含空格,但这会导致回溯

标签: regex visual-studio-2010 xpath


【解决方案1】:

我通常会自动同意像 ltux's 这样的答案,但在这种情况下不会。

您说交替组是动态生成的。它多久动态生成一次?如果是每个用户请求,那么对构建表达式的对象进行快速排序(首先按最长长度,或按字母顺序倒序)可能比编写将(Car|Car Red|Car Blue) 转换为(Car( Red| Blue)?) 的东西更快。

正则表达式可能需要更长的时间(您甚至可能不会注意到正则表达式的速度差异)但组装操作可能会快得多(取决于交替列表的数据源架构)。

在使用 702 个选项对交替进行的简单测试中,在三种方法中,使用这样的选项集的结果是可比较的,但这些结果都没有考虑到构建字符串的时间量,该时间量随着复杂性的增加而增加的字符串增长。

选项都是一样的,只是格式不同

  • 电击
    • 是的
    • 施乐
    • ...
    • 苹果
  • 是的
    • 是的
    • 施乐
    • ...
    • 苹果
  • 施乐
    • 是的
    • 施乐
    • ...
    • 苹果
  • ...
  • 苹果
    • 是的
    • 施乐
    • ...
    • 苹果

使用 Google Chrome 和 Javascript,我尝试了 三种(编辑:四种)不同的格式,并在 0-2 毫秒之间看到了一致的结果。

  • 'Optimized factoring'a(?:4|3|2|1)?
  • Reverse alphabetically sorting(?:a4|a3|a2|a1|a)
  • Factoringa(?:4)?|a(?:3)?|a(?:2)?|a(?:1)?。所有这些都始终以 0 到 2 毫秒的速度进入(不同之处在于我的机器目前可能在做什么,我想)。
  • 更新:我找到了一种方法,您可以在不使用正则表达式进行排序的情况下执行此操作,使用前瞻 like this (?=a|a1|a2|a3|a4|a5)(.{15}|.(14}|.{13}|...|.{2}|.) 其中 15 是上限,一直计数到下限边界。
    • 如果不限制这种方法,我觉得它会导致很多问题和误报。这将是我最不喜欢的结果。如果前瞻匹配,则捕获组(.{15}|...)在任何情况下都将捕获比您期望的更多的东西。换句话说,它将超越比赛。

尽管与我的因式分解示例相比,我编造了术语优化因式分解,但出于任何原因,我都不能推荐我的因式分解示例语法。排序将是最合乎逻辑的,并且比利用前瞻更容易阅读/维护。

您对数据的了解不多,但如果子选​​项可能包含空格并可能重叠,您可能仍需要对子组或因子进行进一步排序,从而进一步降低“优化因子”的价值。

编辑:为了清楚起见,我正在提供一个彻底的检查,说明为什么没有任何形式的因式分解在这里是有益的。至少不是我能看到的任何方式。一个简单的Array.Sort().Reverse().Join("|") 正好给出了在这种情况下任何人都需要的东西。

【讨论】:

  • 感谢您的回答、解释,尤其是指向 regex101 的示例链接。尽管这些解决方案很聪明,但在我看来,它们比我构建它时仅按 OR'd 列表的长度排序更复杂,将最长的放在列表的开头。确保子集(例如“Car”)始终遵循超集(例如“Car Blue”)。谢谢
  • @HexaGamnon 嘿,没问题。虽然我仍然不明白如何按长度排序值比按字母顺序反向排序更容易 VB 示例:Dim arr As String() = {"car", "1", "Car blue", "car red", "a", "car cyan", "Car Orange", "b"}Array.Sort(arr)Array.Reverse(arr)Debug.Print(String.Join("|", arr)) 给你car red|Car Orange|car cyan|Car blue|car|b|a|1 任何句号匹配(如“汽车”)更进一步比他们可能阻止正确匹配的任何东西,比如Car Blue。不管怎样,祝你有个愉快的夜晚。
  • 你说得对。喜欢你的 vb 代码。没想过这样使用Array;我确信 C++/CLI 也有类似的机制。 “太复杂”是指您的嵌套解决方案比对 OR 列表进行排序更复杂。无论如何,我希望有一个聪明的小 RegEx 开关可以解决我的问题,但是,你和@ltux 是对的,唯一的方法是改变对象名称的 OR 列表的结构。谢谢!
  • @HexaGamnon 哦,在这种情况下,我绝对不支持嵌套解决方案。我只是为为什么嵌套解决方案的努力没有提供可衡量的好处提供了彻底的辩护。可读性,这可能是一个因素,在这里不是一个问题,因为没有人会手动编辑正则表达式的一部分。使用我的 702 选项,我只是展示了正则表达式可以轻松处理大型列表(如果您有大型列表)。
  • 绝对同意。 OP 大大简化了。实际的 RegEx 有 1,600 个字符长,其中数百个字符与单个文件写入相比显得苍白无力。 :-0 可读性、简单性和优雅的代码对我来说是一件大事。 感谢您为此付出的所有努力。
【解决方案2】:

正则表达式的| 运算符通常在底层使用Aho–Corasick 算法。它总是停在它找到的最左边的匹配处。我们无法更改 | 运算符的行为。 所以解决方法是避免使用| 操作符。不要使用(Car Blue|Car)(Car|Car Blue),而是使用(Car( Blue)?)

(?<TheObject>(Car( Blue)?) +(?<OldState>.+) +---> +(?<NewState>.+)

那么&lt;TheObject&gt; 组在Blue 存在的情况下将始终为Car Blue

【讨论】:

  • 我怀疑你和@cfqueryparam 是对的。唯一的方法是更改​​ OR 列表的顺序或结构。绝对有帮助。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-03
  • 1970-01-01
相关资源
最近更新 更多