【问题标题】:Using different names for subpatterns of the same number with preg_replace_callback使用 preg_replace_callback 对相同编号的子模式使用不同的名称
【发布时间】:2018-03-27 13:08:30
【问题描述】:

我很难弄清楚我的regex 子模式中的编号到底是什么。我收到了PHP 警告:

PHP Warning: preg_replace_callback(): Compilation failed: different names for subpatterns of the same number are not allowed

尝试以下操作时:

$input = "A string that contains [link-ssec-34] and a [i]word[/i] here";
$matchLink = "\[link-ssec-(0?[1-9]|[1-9][0-9]|100)\]";
$matchItalic = "\[i](.+)\[\/i]";
$output = preg_replace_callback(
    "/(?|(?<link>$matchLink)|(?<italic>$matchItalic))/",
    function($m) {
        if(isset($m['link'])){
            $matchedLink = substr($m['link'][0], 1, -1);
            //error_log('m is: ' . $matchedLink);
            $linkIDExplode = explode("-",$matchedLink);
            $linkHTML = createSubSectionLink($linkIDExplode[2]);
            return $linkHTML;
        } else if(isset($m['italic'])){
            // TO DO
        }

    },
    $input);

如果我删除命名的捕获组,如下所示:

"/(?|(?:$matchLink)|(?:$matchItalic))/"

没有警告,我得到了很好的匹配,但不能在我的函数中有条件地定位它们。我相信我正在遵循正确的程序来命名捕获组,但 PHP 说它们使用相同的 子模式编号,这是我迷失的地方,因为我不确定编号是什么。我熟悉使用 $1$2 等处理子模式,但在与命名组一起使用时看不到这里的相关性。


目标

如果我使用了完全错误的技术,我应该包括我的目标。我最初使用preg_replace_callback() 来替换与这样的模式匹配的标记字符串:

$output = preg_replace_callback(
    "/\[link-ssec-(0?[1-9]|[1-9][0-9]|100)\]/",
    function($m) {
        $matchedLink = substr($m[0], 1, -1);
        $linkIDExplode = explode("-",$matchedLink);
        $linkHTML = createSubSectionLink($linkIDExplode[2]);
        return $linkHTML;
    },
    $input);

需求已经发展到需要匹配同一段落中的多个标签(我的原始示例包括下一个 [i]word[/i]。我不是从头开始为每个模式解析整个字符串,而是尝试查找所有单次扫描段落/字符串中的模式,相信它对系统的负担会更少。研究它使我相信在branch reset中使用命名的捕获组是最好的方法能够使用条件语句定位匹配项。也许我在这条路上走错了路,但希望能找到更好的方法。

想要的结果

$input = "A string that contains [link-ssec-34] and a [i]word[/i] here";
$output = "A string that contains <a href='linkfromdb.php'>Link from Database</a> and a <span class='italic'>word</span> here."

可以根据需要以包含单词或自包含的方括号格式添加更多模式。

【问题讨论】:

  • 为什么你使用branch reset (?| 使用(?: 非捕获组,或者在不需要时删除外部组。
  • @bobblebubble 为了清楚起见,我在编辑中添加了我的理解
  • @biscuitstack 你已经添加了你的 goal 但你从来没有真正定义过最终目标是什么(你基本上已经指出你的最终过程是什么以及你的问题的前提,但不是最终结果)。你到底想用[link-ssec-XX][i][/i] 来做什么?您是否以某种方式编辑原始字符串?如果是,输出是什么样的?
  • @biscuitstack 我认为您的结果中有错字。你的意思是&lt;span class="italic"&gt;word&lt;/span&gt;
  • @ctwheels 我的示例表明我需要区分 [link-ssec-XX][i]word[/i] 是否匹配,而 (a) 替换匹配的内容 (b) 需要回调以使用匹配模式的部分, (c) 在系统开销方面尽可能高效,而不是运行多个preg_replace_callback() 函数。我将任何匹配的字符串转换为无关紧要的内容,并且可能会使问题变得臃肿,但我这样做只是为了以防万一。见编辑。谢谢。

标签: php regex preg-replace-callback


【解决方案1】:

回答您关于警告的问题:

PHP 警告:preg_replace_callback():编译失败:不允许相同编号的子模式使用不同名称

您的模式定义了命名匹配组。但是您的模式也使用了交替(|),这意味着模式的整个部分不需要全部匹配。

这意味着,命名模式link 可以与匹配号1 一起出现,但italic 也可以与匹配号1 一起出现。

由于两者之间存在交替,因此匹配只能是相同的“数字”,因此它们只能具有相同的名称:

@(?|(?<first>one)|(?<first>two))@

将被允许。

@(?|(?<first>one)|(?<second>two))@

抛出此警告。

【讨论】:

  • 你说的是真的,但也很烦人,我看不出有什么理由。那么你能提供一个解决方法吗?日期示例,由于这个确切的问题而无法正常工作:/^(?|(?&lt;y&gt;\\d\\d(\\d\\d)?)-(?&lt;m&gt;\\d\\d?)-(?&lt;d&gt;\\d\\d?)|(?&lt;d&gt;\\d\\d?)\\.(?&lt;m&gt;\\d\\d?)\\.(?&lt;y&gt;\\d\\d(\\d\\d)?))$/
  • 找到解决方案:不要使用(?|,而是设置J 标志,如/...regex.../J。向 Electric Darrell 提供答案here
【解决方案2】:

在没有完全理解我所做的事情的情况下(但现在将对其进行研究),我对@bobblebubble 评论进行了一些试验和错误,并得到了以下结果以产生所需的结果。我现在可以使用针对命名捕获组的条件语句来决定对匹配采取什么操作。

我将regex 更改为以下内容:

$matchLink = "\[link-ssec-(0?[1-9]|[1-9][0-9]|100)\]"; // matches [link-ssec-N]
$matchItalic = "\[i](.+)\[\/i]"; // matches [i]word[/i]
$output = preg_replace_callback(
        "/(?<link>$matchLink)|(?<italic>$matchItalic)/",
        function($m) { etc...

希望它也是一种有效的方法,在开销方面,将多个正则表达式模式与同一字符串中的回调进行匹配。

【讨论】:

  • 分支重置组用于交替共享相同的组索引(example)。如果您使用此功能并命名组,则它需要具有相同的名称。如果您想区分组(这是默认行为),使用此功能看起来很矛盾。
猜你喜欢
  • 2019-02-05
  • 2017-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-14
  • 2016-10-14
  • 1970-01-01
相关资源
最近更新 更多