gpt4 book ai didi

regex - 防止前后查找匹配重叠

转载 作者:行者123 更新时间:2023-12-05 00:21:16 24 4
gpt4 key购买 nike

我正在尝试匹配引号之间包含的字符串文字的所有部分。

(?<=[\"]).*?(?=(?<=[^\\])[\"]{1})

上面是一个工作的正则表达式,除了一个例外,它当然会匹配字符串文字的所有部分,在字符串的左右两侧都有一个引号,而不考虑引号对。

例如(星号表示匹配的字符):

Hello "my" name is "Andy", nice to meet you.`
** ********* ****


字符串文字部分“ name is”在这里匹配的原因很简单,因为它的两边都有引号。这对于我们要寻找的是不正确的。理想的结果是:

Hello "my" name is "Andy", nice to meet you.`
** ****


完全理解这是可能的,也许应该通过编写状态引擎来完成-我的问题是-以正则表达式而言-甚至可能,我如何防止后向匹配之前与外观匹配的字符串文字部分,先?

最佳答案

序幕

我使用Ruby是因为您说您没有偏好,这更是个人兴趣,而不是实际的实时产品。但是,请注意,尽管此处使用的一些技巧可能无法在各种正则表达式引擎上使用(例如一个javascript具有),或者对于相同的事物使用不同的语法,但此处没有使用特定于Ruby的技巧。相同的正则表达式将在Perl,Sublime Text和其他更多地方工作。

但是在我们开始之前...

免责声明:这不是这样做的方法!不要在生产代码库中使用它!


现在我们解决了这个问题...这是一个非常有趣的问题。像其他任何复杂问题一样,divide and conquer是方法。

我们将要使用的技巧:


命名组


正如可以使用(group_contents)创建编号组一样,也可以使用(?<group_name>group_contents)定义命名组。从技术上讲,我们并不需要它,但是它将使所有方式都更加全面。


重新执行组模式


您可以使用\g<group_name_or_number>执行之前定义的相同模式。例如:

(?<three_letter_word>\b\w{3}\b) \g<three_letter_word>


将匹配 xyz abc


重复零次


乍一看, {0}似乎毫无用处。但是,结合以上两者,它可以像不定义函数那样执行某些工作。例如:

(?<even>[02468]){0}7\g<even>8\<even>9\<even>0


将匹配 7x8y9z0,其中 xyz是偶数位。


删除匹配的字符


在许多正则表达式引擎中,常见的限制是您不能定义可变长度的回溯。即使在可以的地方(例如在Java中),您仍然必须定义最大长度。因此,您不能做 (?<=x*)之类的事情。

\K进行救援。 \K的基本含义是删除到目前为止已匹配的所有内容。因此,换句话说, (?<=x*)y可以重写为 x*\Ky

有了这些技巧,让我们开始吧。

首先,让我们定义一些“功能”(使用技巧3)。


escaped_quote


转义的引号是 ",其后是奇数个反斜杠( \)。反斜杠具有转义字符的特殊含义,因此,要匹配单个反斜杠,我们需要将其与另一个反斜杠(也称为 \\ =一个文字反斜杠)进行转义。

要匹配偶数个反斜杠,我们可以做 \\{2}*(又称两个反斜杠零次或多次-2 * n)。奇怪的是,我们只添加了一个反斜杠- \\\\{2}*(2 * n +1)。

我们还将要说的是,我们要按此顺序匹配所有反斜杠。这是因为正则表达式引擎将尝试找到偶数个反斜杠以使我们无法解决,除非我们另行通知。 \\\"将被解释为非转义引号,因为它可以匹配 \\",而忽略第一个斜杠。为了避免这种情况,我们将在后面添加一个负向外观,如下所示: (?<!\\)\\\\{2}*

我们的escaped_quote“ function”的最终定义如下:

(?<escaped_quote>(?<!\\)\\\\{2}*"){0}



非报价


我们要表达的另一件事是没有有意义的引号。这是一个字符序列,它们是转义的引号或根本不是引号。

请注意,对于所有没有引号的内容,我们需要为escaped_quote添加一个负前瞻。这是为了确保我们不会吃掉转义报价中的第一个 \,这将使我们剩下一个未转义的报价。

(?<non_quoting>(?:\g<escaped_quote>|(?!\g<escaped_quote>)[^"])*){0}



平衡报价


我们将需要的最后一个“函数”是一个序列,没有不匹配的引号。这可以是根本没有有意义的引号的东西,也可以是偶数个有意义的引号:

(?<balanced_quotes>\g<non_quoting>|(?:\g<non_quoting>"\g<non_quoting>){2}+){0}



完成所有准备工作后,我们已准备好进行匹配。

我们将从字符串的开头或单引号开始。前者是显而易见的。后者是因为我们的比赛将留下一个报价。 (?:^|")

编辑:事实证明这些还不够。对于上次我们匹配空字符串的情况, \K将不允许我们停留在相同位置,并且在临时的后向匹配中再次匹配空字符串。为了解决这个问题,我们将添加另一个替代方法-空字符串。请注意,这里的顺序很重要,因此我们仅在其他两个失败的情况下使用此替代方法: (?:^|"|)

接下来是一个non_quoting序列,并且丢弃了所有内容(使用#4技巧)以实现回望:

(?:^|"|)\g<non_quoting>"\K


在那之后,我们实际匹配的是一个non_quoting序列:

(?:^|"|)\g<non_quoting>"\K\g<non_quoting>


最后,我们必须确保在关闭当前引号之后,在字符串的末尾留有balanced_quotes:

(?:^|"|)\g<non_quoting>"\K\g<non_quoting>(?="\g<balanced_quotes>$)




最后!

我们可以将“功能”定义和实际匹配项加在一起,以实现最终的正则表达式:

(?<escaped_quote>(?<!\\)\\\\{2}*"){0}(?<non_quoting>(?:\g<escaped_quote>|(?!\g<escaped_quote>)[^"])*){0}(?<balanced_quotes>\g<non_quoting>|(?:\g<non_quoting>"\g<non_quoting>){2}+){0}(?:^|"|)\g<non_quoting>"\K\g<non_quoting>(?="\g<balanced_quotes>$)


See it in action

最后的想法

这里要注意的一点是,即使您的正则表达式引擎不支持某些功能,也可以通过替换函数调用来实现相同的正则表达式。 \K是到处都看不到的唯一需要的东西。

我希望这对每个阅读本文的人来说都是一次有趣的学习经历。

关于regex - 防止前后查找匹配重叠,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32074828/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com