【问题标题】:Strange behavior of preg_replacepreg_replace 的奇怪行为
【发布时间】:2013-02-20 03:18:59
【问题描述】:

我想切换链接的协议。如果是http,应该变成https,https应该变成http。我正在使用 pre_replace 但出了点问题。

有人可以看看我的代码并告诉我在思考过程中缺少什么吗?

代码如下:

           $pattern = array(
                0 => '/^(http\:)/',
                1 => '/^(https\:)/'
            );
            $replace = array(
                0 => 'https:',
                1 => 'http:'
            );

            ksort($pattern);
            ksort($replace);

            $url = 'http://someurl.com';

            echo $url."<br />";

            $url = preg_replace($pattern, $replace, trim($url),1);

            die($url);

【问题讨论】:

标签: php regex


【解决方案1】:
  1. : 不需要转义,它不是特殊字符。
  2. 您不需要捕获组()
  3. 您无需调用ksort(),您的数组在声明时已按关键字排序。
  4. 您的代码似乎将“http”替换为“https”并且将“https”替换为“http”。为什么?

如果您只是想强制使用 https,$url = preg_replace('/^http:/', 'https', trim($url)); 可以正常工作。

编辑

我仍然不知道为什么有人想同时切换 http/https,但是你去吧:

function protocol_switcheroo($url) {
  if( preg_match('/^http:/', $url) ) {
    return preg_replace('/^http:/', 'https:', $url); // http to https
  } else if( preg_match('/^https:/', $url) ) {
    return preg_replace('/^https:/', 'http:', $url); // https to http
  } else {
    return $url; // for URIs with protocols other than http/https
  }
}

您需要将要替换的调用分开,这样您就不会像问题中的原始代码那样意外地将它们链接起来。

【讨论】:

  • 投反对票的不是我,但我怀疑原因是 OP 明确想要交换 http 和 https —— 即如果你有一个,然后切换到另一个。我也不知道你为什么要这样做,但这是他想要的,你的第 4 点对此没有帮助。 (第 1-3 点是有用的奖励,但实际上与问题的实质无关)
  • @VladL 我从来没有对你的答案投过票。我们俩都被一票否决,我愿意由同一个人下注。
  • @Sammitch 谢谢。这很有用,实际上我在问题中的代码不起作用后所做的事情。对于您的第四点,我认为它会被 ':' 字符和索引阻止。 ksort() 是我第一次尝试的其余部分;)
  • @ThomasVenturini 你真的不需要正则表达式来执行此操作,因为它比使用 strpos()substr_replace() 等函数更慢且效率更低
  • @crypticツ 是的,但不能在我的 php 类中使用,我希望将所有模式和替换作为属性
【解决方案2】:

这不适用于 http -> https(但适用于 https -> http)的原因是 preg_replace() 首先使用第一组键/变量 (0) 将 http 更改为 https,然后立即返回 https -> http,因为这样每个数组中的第二组变量 (1) 是另一个有效匹配项。

【讨论】:

    【解决方案3】:

    您的网址被替换了两次。首先,第一个表达式匹配并且http://someurl.com 变为https://someurl.com。然后,第二个表达式匹配,https://someurl.com 变为 http://someurl.com

    这个例子更容易看出:

    echo preg_replace(
        array('/fox/',  '/turtle/'),
        array('turtle', 'sparrow'),
     'fox', 1);
    

    ...打印sparrow

    【讨论】:

      【解决方案4】:
      //$url = 'http://example.com/https://www';
      $url = 'https://example.com/http://www';
      
      $url = (0 === strpos($url, 'http:'))
          ? substr_replace($url, 's:', 4, 1)
          : substr_replace($url, '', 4, 1);
      
      echo $url;
      

      这将转换HTTP -&gt; HTTPSHTTPS -&gt; HTTP

      它不使用速度较慢的正则表达式,也不使用str_replace(),它可能会无意中替换 URL 的其他部分。它只会替换第一个前缀。

      Breakdown :它查看 URL 是否以 http: 开头,是否将第 5 个字符 : 替换为 s: 使其成为 HTTPS。否则它将替换第 5 个字符 s,而没有任何内容使其成为 HTTP。

      【讨论】:

        【解决方案5】:

        您遇到的问题是preg_replace() 一个接一个地进行两个替换,因此在第一个运行后,第二个将反转第一个的效果。

        您需要在一个表达式中指定这两种模式才能让它们一起运行。

        我建议使用preg_replace_callback() 而不是preg_replace()。有了这个,您可以编写更复杂的输出表达式,从而更容易将它们组合成一个模式。像这样的东西可以解决问题:

        $outputString = preg_replace_callback(
            '/^(http|ftp)(s)?(:)/',
            function($matches) {return $matches[1].($matches[2]=='s'?'':'s').':';},
            $inputString
        );
        

        希望对您有所帮助。

        [编辑] 在 OP 发表评论后,编辑了代码,使其适用于 ftp/ftpshttp/https

        【讨论】:

        • 谢谢,最亲切的回答。想给你一些麻烦,但有人搞砸了我的声誉,所以我不能:S 如果我只需要检查这两个协议,这将是完美的,但还会有更多。非常感谢^^
        • @ThomasVenturini - 为您解决了这个问题。无论如何,您的问题不值得-3;这里似乎有一些驱动式的投票,所以希望投票可以平衡一些事情。对于它的价值,这个答案可以扩展以适应多种协议;我将对其进行编辑,使其适用于ftp/ftps 以及http/https,您会看到它是多么容易适应。
        猜你喜欢
        • 2016-09-12
        • 2012-10-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-08
        • 2015-07-20
        • 2010-10-03
        • 2021-07-12
        相关资源
        最近更新 更多