正则表达式:如何替换不以某些东西开头的字符串?

时间:2011-08-16 17:15:12

标签: regex

我需要使用不同的根相对URL替换根相对URL:

/Images/filename.jpg

应替换为:

/new/images-dir/filename.jpg

我开始使用PHP的str_replace函数:

$newText = str_replace('/Images/', '/new/images-dir/', $text);

...但后来我意识到它正在替换我想要替换的绝对网址:

http://sub.domain.com/something/Images/filename.jpg
#...is being replaced with...
http://sub.domain.com/something/new/images-dir/filename.jpg

然后我转而使用PHP的preg_replace函数,这样我就可以使用正则表达式来有选择地只替换根相对URL而不是绝对URL。但是,我似乎无法弄清楚这样做的语法:

$text = 'There is a root relative URL here: <img src="/Images/filename.jpg">'
      . 'and an absolute here: <img src="http://sub.domain.com/something/Images/filename.jpg">'
      . 'and one not in quotes: /Images/filename.jpg';
$newText = preg_replace('#/Images/#', '/new/images-dir/', $text);

如何编写正则表达式以便忽略任何绝对URL并仅替换根相对URL?

3 个答案:

答案 0 :(得分:2)

在进行三次编辑以得到正确的正则表达式后,我得出结论,我的第一个答案是最好的。 PHP's string functions比正则表达式更适合此任务:

使用str_replace():

function match($value)
{
   // The second condition is probably unnecessary,
   // unless your path argument is incorrectly formatted
   if( ($value[0] != "/") || (stristr($value, "http:") != FALSE) )
   {
      return $value;
   }
   return str_replace("/Images/", "/new/images-dir/", $value);
}

str_replace()的优点是可读性。

如果读者不理解正则表达式,他们仍然可以清楚地看到匹配的条件:输入字符串必须以“/”开头,并且不得包含“http:”

此外,搜索键和替换字符串都以纯文本清楚地表示。

使用preg_replace():

function match($value)
{
   $pattern = "/^(\/((.+?)\/)*?)Images\//";

   // Assuming value is a root-relative path, everything
   // before "Images/" should be capured into back-reference 1;
   // The replacement string re-inserts it before "new/images-dir/"
   return preg_replace($pattern, "\\1new/images-dir/", $value);
}

正则表达式尝试匹配以下内容:

  1. 将字符串的开头与^
  2. 匹配
  3. 后跟正斜杠以指示根相对URL
  4. 然后重复零{或}更多lazily quantified次 小组((.+?)/)。该组由一个或多个懒惰量化字符和另一个正斜杠组成。
  5. 匹配后续字符串“Images”和最后的正斜杠。
  6. 两个match()函数在测试时的运行方式如下:

    match("http://test/more/Images/file"); // Returns original argument
    match("/test/more/Images/file");       // Returns with match replaced
    

答案 1 :(得分:1)

如您所示,根相对链接通常在引号内。因此,请在报价上进行匹配,然后将其重新置于替代品中。

$text = 'There is a root relative image here: <img src="/Images/filename.jpg">';
$newText = preg_replace('#"/Images/#', '"/new/images-dir/', $text);

<强>更新

如果您有两种不同的情况,请尝试两种不同的特定的替换,而不是尝试设计一个完美的。让我们知道其他案例是什么。

如果你需要匹配更多,那么你正在寻找一个“负面的lookbehind断言”,所以你要确保它与之前的“http:// blah”部分不匹配。 lookbehind的问题是它需要一个静态字符串匹配...它不能有可变长度。 http://www.php.net/manual/en/regexp.reference.assertions.php

这样的事情可能有用,如果你主要使用.net和.com链接的链接,而图像部分位于根目录:

$text = 'There is a root relative image here: <img src="/Images/filename.jpg">';
$newText = preg_replace('#(?<=.net|.com|.org|.cc)/Images/#', '/new/images-dir/', $text);

答案 2 :(得分:1)

根据PHP documentation on Lookbehind assertions

  

Lookbehind断言以(?<=开头,用于肯定断言,(?<!用于否定断言。

使用这种语法,我能够让它工作:

$text = preg_replace('#(?<!http\://sub.domain.com/something)/Images/#', '/new/images-dir/', $text);