首页 > 解决方案 > preg_match_all 找不到所有匹配项

问题描述

我正在尝试查找包含域的字符串。我有以下模式:

"|s:\\d+:\\\\\"((?:.(?!s:\\d+))+?){$domain}(.+?)\\\\\";|"

这个(模式)似乎有效,但我只得到 PHP 中的前两个匹配项。

$filename = "caciki_tr.sql";
$domain   = "caciki.com.tr";

$domain   = escape($domain, ".");

$content = file_get_contents($filename);

$pattern = "|s:\\d+:\\\\\"((?:.(?!s:\\d+))+?){$domain}(.+?)\\\\\";|";

preg_match_all($pattern, $content, $matches, PREG_SET_ORDER);
print_r($matches);

function escape($string, $chars) {
    $chars = str_split($chars);
    foreach ($chars as $char) {
        $string = str_replace($char, "\\{$char}", $string);
    }
    return $string;
}

Array
(
    [0] => Array
        (
            [0] => s:121:\"/home/caciki/domains/caciki.com.tr/public_html/wp-content/themes/rafine/woocommerce/single-product/product-thumbnails.php\";
            [1] => /home/caciki/domains/
            [2] => /public_html/wp-content/themes/rafine/woocommerce/single-product/product-thumbnails.php
        )

    [1] => Array
        (
            [0] => s:81:\"/home/caciki/domains/caciki.com.tr/public_html/wp-content/themes/rafine/style.css\";
            [1] => /home/caciki/domains/
            [2] => /public_html/wp-content/themes/rafine/style.css
        )

)

只有当我修改目标文件时,我才会得到所有匹配项 (11)。一定有什么东西破坏了模式/PHP。

我在 Python 和 C# 中测试了相同的模式,它们给出了正确的结果:

在此处输入图像描述

在此处输入图像描述

那么这里有什么问题呢?

caciki_tr.sql(目标文件)


更新:这里的模式与不同的子字符串一起使用(例如,域、url、用户名等)。并非目标文件中的所有字符串都遵循相同的模式。例如,URL 的模式应该能够匹配以下内容:

$url = "http://[DOMAIN_OMITTED]/~caciki";
$pattern = "|s:\d+:\\\\\"([^s]*(?:s(?!:\d)[^s]*)*){$url}(.+?)\\\\\";|";

s:28:\"http://[DOMAIN_OMITTED]/~caciki\";
s:28:\"<a href=\"http://[DOMAIN_OMITTED]/~caciki\">some page</a>\";

简而言之,在s:28:\"和子字符串 ($url) 之间或子字符串之后可能没有字符串。所以它应该是可选的。

标签: phpregex

解决方案


当前的模式效率相当低,因为它包含一个损坏的“tempered greedy token(?:.(?!s:\d+))+? ” ,. 如果您想在生产中使用这样的正则表达式,这是一个非常低效的结构,应该“展开”。

您可以使用[^s]*(?:s(?!:\d)[^s]*)*它来代替它:

"|s:\d+:\\\\\"([^s]*(?:s(?!:\d)[^s]*)*)$domain(.+?)\\\\\";|'
               ^^^^^^^^^^^^^^^^^^^^^^^

细节

  • [^s]*- 0+ 以外的字符s
  • (?:- 一个非捕获组重复...
    • s(?!:\d)-s不跟:+ 一个数字
    • [^s]*- 0+ 以外的字符s
  • )*- 零次或多次。

请注意,如果您打算使用大文件,请确保您的模式尽可能高效。此外,如果您想处理大文件,pcregrep是一个有趣的解决方案(这是一个非常快速的工具)。


推荐阅读