Regex 正则表达式是否匹配未被另一个字符包围的值?

Regex 正则表达式是否匹配未被另一个字符包围的值?,regex,Regex,这是我做过的最艰难的事情之一。多年来,我一直在搜索,但我就是找不到一种方法来做到这一点——匹配一个没有被给定字符包围的字符串,比如引号或大于/小于符号 像这样的正则表达式可以匹配不在HTML链接中的URL、不在引号中的SQL table.column值以及许多其他内容 Example with quotes: Match [THIS] and "something with [NOT THIS] followed by" or even [THIS]. Example with <,&

这是我做过的最艰难的事情之一。多年来,我一直在搜索,但我就是找不到一种方法来做到这一点——匹配一个没有被给定字符包围的字符串,比如引号或大于/小于符号

像这样的正则表达式可以匹配不在HTML链接中的URL、不在引号中的SQL table.column值以及许多其他内容

Example with quotes: 
Match [THIS] and "something with [NOT THIS] followed by" or even [THIS].

Example with <,>, & " 
Match [URL] and <a href="[NOT URL]">or [NOT URL]</a>

Example with single quotes: 
WHERE [THIS] LIKE '%[NOT THIS]'
这里有一个测试模式:像我所想的正则表达式只匹配第一个“引号”

引用“不要引用我,以免我引用你!”


这有点难。有很多方法,只要你不需要跟踪筑巢。例如,让我们避免引用内容:

^((?:[^"\\]|\\.|"(?:[^"\\]|\\.)*")*?)THIS
或者,解释:

^     Match from the beginning
(     Store everything from the beginning in group 1, if I want to do replace
    (?:  Non-grouping aggregation, just so I can repeat it
        [^"\\]  Anything but quote or escape character
        |       or...
        \\.     Any escaped character (ie, \", for example)
        |       or...
        "       A quote, followed by...
        (?:     ...another non-grouping aggregation, of...
            [^"\\]  Anything but quote or escape character
            |       or...
            \\.     Any escaped character
        )*      ...as many times as possible, followed by...
        "       A (closing) quote
    )*?  As many as necessary, but as few as possible
)     And this is the end of group 1
THIS  Followed by THIS
现在,还有其他方法可以做到这一点,但可能没有那么灵活。例如,如果你想找到这个,只要前面没有“/”或“#”序列——换句话说,在注释之外有一个THIS,你可以这样做:

(?<!(?:#|//).*)THIS
preg_match('/THIS(?=(?:(?:[^"]*+"){2})*+[^"]*+\z)/')
(?
这里,
(?是一个否定的后视。它将与这些字符不匹配,但它将测试这些字符是否不在此之前出现


至于任何任意嵌套的结构——例如,n
由n
关闭)
,它们不能用正则表达式来表示。Perl可以这样做,但它不是正则表达式。

好吧,正则表达式对于这一点来说是错误的工具,所以很自然很难做到

被其他事物“包围”的事物不是规则语法的有效规则。大多数(可以说是所有严肃的)标记和编程语言都不是规则的。只要不涉及嵌套,您就可以用正则表达式模拟解析器,但一定要了解您在做什么


对于HTML/XML,只需使用HTML resp.XML解析器;这些解析器几乎适用于任何语言或web框架;使用它们通常只需要几行代码。对于表格,您可能可以使用CSV解析器,或者在必要时,使用您自己的解析器来提取引号内/外的部分。提取您感兴趣的部分后,您可以使用简单的字符串比较或正则表达式来获得结果。

最佳解决方案将取决于您对输入的了解。例如,如果您正在查找不包含在双引号中的内容,这是否意味着双引号将始终保持适当的平衡?它们可以用反斜杠或enclo转义吗用单引号唱

假设最简单的情况——无嵌套,无转义——您可以使用如下前瞻:

(?<!(?:#|//).*)THIS
preg_match('/THIS(?=(?:(?:[^"]*+"){2})*+[^"]*+\z)/')
在找到目标(这一点)后,前瞻基本上计算该点之后的双引号,直到字符串结束。如果双引号的数量为奇数,则匹配必须发生在一对双引号内,因此无效(前瞻失败)

正如您所发现的,这个问题不太适合正则表达式;这就是为什么所有建议的解决方案都依赖于在真正的正则表达式中找不到的特性,如捕获组、环顾四周、不情愿和占有量词。我甚至不会在没有或没有它们的情况下尝试这一点

编辑:要将此解决方案扩展到可以用反斜杠转义的双引号,只需替换正则表达式中与“任何非双引号的内容”匹配的部分:

使用“任何不是引号或反斜杠,或反斜杠后跟任何内容的内容”:

由于反斜杠转义序列相对较少,因此在正则表达式的该部分中,尽可能多地匹配未转义字符是值得的:

(?:[^"\\]++|\\.)
综合起来,正则表达式变成:

'/THIS\d+(?=(?:(?:(?:[^"\\]++|\\.)*+"){2})*+(?:[^"\\]++|\\.)*+$)/'
应用于您的测试字符串:

'Match THIS1 and "NOT THIS2" but THIS3 and "NOT "THIS4" or NOT THIS5" ' +
'but \"THIS6\" is good and \\\\"NOT THIS7\\\\".'

…在考虑嵌套元素(“a”this和“this”)和反斜杠项目“\”this\”之后,它应该匹配
'THIS1'
'THIS3'
'THIS4'
'THIS6'

"这似乎真的不是正则表达式的工作。然而,我能想到的解决这个问题的唯一方法是使用一个类似正则表达式的逐字符解析器,在查找和输入有效的引号或子引号时,它会标记$quote_level=####。这样,在字符串的这一部分,您就可以知道自己是否在其中任何给定的字符,即使它是通过斜杠或其他方式转义的

我想使用这样的逐字符解析器,您可以标记开始/结束引号的字符串位置,这样您就可以按引号分段,只处理引号之外的部分

下面是一个例子,说明这个解析器需要足够智能才能处理嵌套级别

Match THIS and "NOT THIS" but THIS and "NOT "THIS" or NOT THIS" but \"THIS\" is good.

//Parser "greedy" looking for nested levels
Match THIS and "
            NOT THIS"
                but THIS and "
                        NOT "
                            THIS"
                                or NOT THIS"
                                        but \"THIS\" is good

//Parser "ungreedy" trying to close nested levels
Match THIS and "        " but THIS and "    " THIS "            " but \"THIS\" is good.
                NOT THIS                NOT          or NOT THIS


//Parser closing levels correctly.
Match THIS and "        " but THIS and "                    " but \"THIS\" is good.
                NOT THIS                NOT "   " or NOT THIS
                                            THIS

请参阅Perl和。

正如Alan M所指出的,您可以使用正则表达式查找奇数,从而通知您在任何给定字符串内部或外部的位置。以引号为例,我们似乎非常接近解决此问题的方法。剩下的唯一一件事就是处理转义引号。(我肯定嵌套引号几乎是不可能的)


这取决于您使用的正则表达式的类型-是否允许正/负前瞻/behindI假设了完整的正则表达式功能,如PHP、Perl等…+1正是我要指出的。基本上,这个问题很难,就像用锤子钻一个洞很难一样。“包围”对于常规语言来说,这是一个非常有效的规则。嵌套和取消测试则不然。@Daniel(右)常规语法中的有效规则只是那些在左侧正好有一个非终结符,或者是空字符串,或者是一个终结符,或者是一个终结符后跟一个非终结符的规则。Q:=“S;S:=aE;S:=bE;…;S:=zE;E:=”--这是一个用引号括起来的小写字母,在常规语法中。还有别的吗?这就是我“模拟解析器”的意思。这是这个主题的一个很好的开始,但我担心它只有在三个引号(“)时才会查找“This”远离绳子的末端。哎呀!我漏掉了一组o
Match THIS and "NOT THIS" but THIS and "NOT "THIS" or NOT THIS" but \"THIS\" is good.

//Parser "greedy" looking for nested levels
Match THIS and "
            NOT THIS"
                but THIS and "
                        NOT "
                            THIS"
                                or NOT THIS"
                                        but \"THIS\" is good

//Parser "ungreedy" trying to close nested levels
Match THIS and "        " but THIS and "    " THIS "            " but \"THIS\" is good.
                NOT THIS                NOT          or NOT THIS


//Parser closing levels correctly.
Match THIS and "        " but THIS and "                    " but \"THIS\" is good.
                NOT THIS                NOT "   " or NOT THIS
                                            THIS
$string = 'Match THIS1 and "NOT THIS2" but THIS3 and "NOT "THIS4" or NOT THIS5" but \"THIS6\" is good and \\\\"NOT THIS7\\\\".';


preg_match_all('/[^"]+(?=(?:(?:(?:[^"\\\]++|\\\.)*+"){2})*+(?:[^"\\\]++|\\\.)*+$)/', $string, $matches);

Array (
        [0] => Match THIS1 and 
        [1] =>  but THIS3 and 
        [2] => THIS4
        [3] =>  but 
        [4] => THIS6
        [5] =>  is good and \\
        [6] => NOT THIS7\
        [7] => .
    )