【问题标题】:How can I replace the Nth instance of a string in PHP?如何替换 PHP 中字符串的第 N 个实例?
【发布时间】:2013-08-18 03:05:05
【问题描述】:

我需要根据在它的父简码中设置的活动号码,动态地将一个活动类添加到 wordpress 中的简码。

父母看起来像这样:

$active = 3;
$output .= "\n\t\t".wpb_js_remove_wpautop($content);

$content 变量在技术上可以包含短代码标签之间的任何内容,但具体来说,应该包含更多的子短代码(它是一个标签短代码,因此父标签是标签,子标签是标签)。

我需要做的是解析$content并将[vc_tab的第N个($active变量)实例替换为[vc_tab active="true"

我意识到有更好的方法来设计短代码来弥补这一点,但我能做的很有限,因为我正在修改 Visual Composer 以使用 Bootstrap 选项卡而不是 jQuery,而 Bootstrap 选项卡需要两个 @ 上的活动类987654326@ 和 <div class="tab-pane"> 元素,但我不希望用户必须向父简码添加一个活动号码,并为孩子添加一个活动的真/假,因为这会让人感到困惑。

到目前为止,我只能找到替换第一个或所有出现的地方,而不是第 N 个。

我暂时使用 jQuery 来激活适当的 <div>,但这会导致不受欢迎的 FOUC,在加载时没有可见的窗格,然后在 jQuery 运行时弹出一个窗格。

$('.tabs-wrap').each(function(){
  var activeTab = $(this).find('.tabbed li.active a').attr('href');
  $(this).find(activeTab).addClass('active');
});  

【问题讨论】:

    标签: php regex wordpress preg-replace preg-match


    【解决方案1】:

    这是一个有趣的问题,让我想到如果你真的需要一个正则表达式来做到这一点。

    我的结论是我不相信你这样做。您需要做的就是找到Nth $needle 的出现,并将其替换为您的替换。这可以通过一些简单的字符串操作来实现,如下所示:

    1. 定义一些变量。您的输入字符串、您的$active 索引、我们正在寻找的$needle,以及我们将替换Nth 匹配的$replacement。

      $string  = "[vc_tab [vc_tab [vc_tab [vc_tab [vc_tab [vc_tab";
      $active  = 3;
      $needle  = "[vc_tab";
      $replace = '[vc_tab active="true"';
      
    2. 现在,我们需要确保$string中有足够的$needles,否则我们无法进行这种替换。

      if( substr_count( $string, $needle) < $active) {
          throw new Exception("There aren't enough needles in the string to do this replacement");
      }
      
    3. 现在,我们知道我们可以进行替换。所以让我们在Nth 出现的$needle 结束的字符串中找到$index

      $count = 0; $index = 0;
      while( $count++ < $active) {
          $index = strpos( $string, $needle, $index) + strlen( $needle);
      }
      
    4. 现在,$index 在字符串中指向这里:

      [vc_tab [vc_tab [vc_tab [vc_tab [vc_tab [vc_tab
                             ^
                             Here
      

      我们现在可以做一些简单的substr() 来形成最终的字符串:

      $result = substr( $string, 0, $index - strlen( $needle)) . $replace . substr( $string, $index);
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^              ^^^^^^^^^^^^^^^^^^^^^^^^
                Before the Nth match                                        After the Nth match
      

      所以,我们最终连接起来:

      • [vc_tab [vc_tab
      • [vc_tab active="true"
      • [vc_tab [vc_tab [vc_tab

    这导致我们的final output

    [vc_tab [vc_tab [vc_tab active="true" [vc_tab [vc_tab [vc_tab
    

    the demo试试吧!

    【讨论】:

    • 谢谢,这很好用——除了在错误检查中没有抛出异常,我只是设置了$active = 1(恢复为默认值)
    【解决方案2】:

    我不熟悉你提到的工具,所以我不能说是否有更好的方法来实现最终目标,但你可以使用 preg_replace_callback 替换第 N 次出现的东西:

    $active = 3;     // target occurrence
    $occurrence = 0; // counter
    $output = preg_replace_callback(
        '/\[vc_tab/',
        function ($matches) use(&$occurrence, $active) {
            return ++$occurrence != $active
                ? $matches[0]
                : '[vc_tab active="true"';
        },
        $output,
        $active
    );
    

    See it in action.

    除非出现计数器等于目标数,否则通过将每个出现替换为自身来工作。

    请注意,我已将$active 作为要替换的最大出现次数,以获得免费的性能提升;这将导致preg_replace_callback$active 的出现被替换后停止(我们知道之后的所有出现都将被它们自己替换,所以继续下去没有意义)。

    【讨论】:

    • 感谢您的帮助。 preg_replace_callback 看起来是一个有用的函数,需要牢记,但是这段代码在现实世界的 $output 中不起作用,而不是调试它,我使用了下面的字符串操作方法,它可能比正则表达式更有效?
    • @tsdexter:我不确定——可能需要优化低级操作,但我不会冒险猜测(衡量它是否重要)。不知道为什么这对你不起作用,因为代码读起来很干净。
    • 好吧,现在我很好奇,所以我必须弄清楚。
    • 嗯,我一定是在更改变量名时搞砸了——它按照你写的那样工作。我在 2 上做了一些微计时,结果有利于字符串操作。我测试了 1) 3 个标签/短内容 - 2) 11 个标签/长内容 - 和 3) 11 个标签/长内容,最后一个标签处于活动状态,结果是:(s = string, p=preg) 1) s: 0.013113021851s / p:0.025987625122s 2) s:0.015020370483s / p:0.057935714722s 3) s:0.016927719116s / p:0.042915344238s
    猜你喜欢
    • 2017-12-01
    • 1970-01-01
    • 2010-09-13
    • 1970-01-01
    • 2013-11-23
    • 2015-10-24
    • 1970-01-01
    • 1970-01-01
    • 2019-10-15
    相关资源
    最近更新 更多