重置位置指针以替换所有出现的模式,直到使用preg_replace在一步中分隔符为止

时间:2018-08-10 07:07:53

标签: php regex preg-replace

是否可以使用preg_replace替换所有出现的模式,直到指定的定界符为止?

我想替换一个模式的多次出现,而不是定界符之前的整个字符串。

是否可以在不拆分字符串的情况下一步完成此操作? 是否可以指定在每次替换后将位置指针重置为开始位置?我可以使用前瞻性实现吗?

例如,我想替换以下URL中所有出现的//,直到?字符为止。

输入:

https://www.example.com//abc/def/ghi/?jkl=mno//pqr
https://www.example.com//abc/def//ghi/?jkl=mno//pqr
https://www.example.com//abc//def//ghi/?jkl=mno//pqr

预期输出:

https://www.example.com/abc/def/ghi/?jkl=mno//pqr

请注意

  • 主题字符串中的//可能出现零次或多次
  • 在定界符//之后发生的?都不会受到影响。
  • 可以先分割字符串来完成。但我正在寻找仅使用正则表达式的解决方案。

2 个答案:

答案 0 :(得分:3)

您可以使用正向前瞻来确保//后跟?

$urls = array('https://www.example.com//abc/def/ghi/?jkl=mno//pqr',
'https://www.example.com//abc/def//ghi/?jkl=mno//pqr',
'https://www.example.com//abc//def//ghi/?jkl=mno//pqr');
foreach ($urls as $url)
    echo preg_replace('#//(?=.*\?)#', '/', $url) . "\n";

输出:

https:/www.example.com/abc/def/ghi/?jkl=mno//pqr
https:/www.example.com/abc/def/ghi/?jkl=mno//pqr
https:/www.example.com/abc/def/ghi/?jkl=mno//pqr

修改

正如@revo指出的那样,这也在//之后删除https:。为避免这种情况,请在后面添加一个负数:

foreach ($urls as $url)
    echo preg_replace('#(?<!https:)//(?=.*\?)#', '$1/', $url) . "\n";

输出:

https://www.example.com/abc/def/ghi/?jkl=mno//pqr
https://www.example.com/abc/def/ghi/?jkl=mno//pqr
https://www.example.com/abc/def/ghi/?jkl=mno//pqr

答案 1 :(得分:3)

当前接受的答案足以解决问题,但存在一些可能在不久的将来引起问题的问题:

  • 真的在第一次出现?

  • 后并没有立即停止匹配
  • 它仅适用于https协议(您需要手动添加其他对象以向后看)。

正则表达式:

(^\w+:/|\G[^?/]*)/+

上面的正则表达式调用\G,它匹配上一个匹配结束的位置。这意味着当找到?时,它将无法继续匹配。

请参见live demo here

PHP:

echo preg_replace('@(^\w+:/|\G[^?/]*)/+@', '$1/', $url);

请注意,如果交替的第一面可能无法满足要求,例如,您可能需要在(?!^)之前先\G。在://example.com