无关的空尾追踪

时间:2016-11-11 16:59:54

标签: php regex

我试图创建一个用于拆分字符串的正则表达式,但遗憾的是我的要求比简单拆分要复杂一些,所以我不能在PHP中使用preg_split()

所以我正在做的是在子表达式中匹配我的分隔符(或者更确切地说,它们的一部分),在另一个子表达式中匹配它前面的所有内容,并将字符串的结尾视为分隔符这个目的。考虑到这一点,我想出了以下内容:

([^?;]*)(?|\?([0-9]*)|(;)|$)

正如您所希望看到的那样,第一个子模式会查找一块没有任何分号问号的文本。在此之后,我有一个子模式匹配任何问号后跟一个可选数字(存储),或者是分号(存储)或字符串结尾。

问题在于我似乎在字符串结尾处得到一个无关紧要的空匹配,如下所示:

$sql = 'CALL foo(?0, ?1, ?2, ?3)';
preg_match_all('/([^?;]*)(?|\?([0-9]*)|(;)|$)/', $sql, $matches);
print_r($matches);

生成如下所示的输出:

Array
(
    [0] => Array
        (
            [0] => CALL insert_host(?0
            [1] => , ?1
            [2] => , ?2
            [3] => , ?3
            [4] => )
            [5] => 
        )

    [1] => Array
        (
            [0] => CALL insert_host(
            [1] => , 
            [2] => , 
            [3] => , 
            [4] => )
            [5] => 
        )

    [2] => Array
        (
            [0] => 0
            [1] => 1
            [2] => 2
            [3] => 3
            [4] => 
            [5] => 
        )

)

请注意$matches[0][5]下的空白匹配;我希望在匹配括号后满足字符串大小写的结束,导致没有进一步的匹配,但它继续产生另一个匹配,我无法弄清楚原因。

所以我的问题是;为什么这里会产生额外的匹配,我该如何防止呢?

注意:我已经考虑过要求字符串大小写的结尾至少有一个字符,但这并不好,因为事实上我想要一个空的结果通配符位于字符串的位置,因为我试图模拟拆分函数的行为。例如,如果输入为SELECT ?,我希望匹配SELECT ?加上一个空字符串。这里的想法是,一旦我处理了任何匹配的分号,我就可以简单地用implode('?', $matches[1])来重现带有数字通配符的语句。

1 个答案:

答案 0 :(得分:0)

我相信我可能已经找到了解决问题的具体案例的替代方案;我所做的是翻转表达式,使得分隔符首先匹配,或者失败,字符串的开头,如下:

(?|\?([0-9]*)|(;)|^)([^?;]*)

这会在所有情况下产生预期结果:

preg_match_all('/(?|\?([0-9]*)|(;)|^)([^?;]*)/', 'CALL foo(?3, ?2, ?1, ?0)', $matches);
print_r($matches);

产地:

Array
(
    [0] => Array
        (
            [0] => CALL foo(
            [1] => ?3, 
            [2] => ?2, 
            [3] => ?1, 
            [4] => ?0)
        )
    [1] => Array
        (
            [0] => 
            [1] => 3
            [2] => 2
            [3] => 1
            [4] => 0
        )

    [2] => Array
        (
            [0] => CALL foo(
            [1] => , 
            [2] => , 
            [3] => , 
            [4] => )
        )
)

虽然:

preg_match_all('/(?|\?([0-9]*)|(;)|^)([^?;]*)/', 'SELECT ?', $matches);
print_r($matches);

产地:

Array
(
    [0] => Array
        (
            [0] => SELECT 
            [1] => ?
        )
    [1] => Array
        (
            [0] => 
            [1] => 
        )
    [2] => Array
        (
            [0] => SELECT 
            [1] => 
        )
)

但是,这只能起作用,因为我知道输入永远不会包含分隔符作为第一个字符;如果我提供一个它会遇到很多相同的问题,所以我不确定是否将其称为真正的解决方案。

我仍然有兴趣知道为什么我的原始表达式会得到一个额外的匹配,因为我希望贪婪的匹配意味着它是不可能的,因为一旦字符串的结尾匹配,应该没有什么可以找到的