【问题标题】:Wildcard replace in PHPPHP中的通配符替换
【发布时间】:2010-12-21 10:11:08
【问题描述】:

我没有在 PHP 中使用正则表达式的经验,所以我通常使用一系列 str_replace()、substr()、strpos()、strstr() 等来编写一些复杂的函数(你懂的)。

这次我想正确地执行此操作,我知道我需要为此使用正则表达式,但对使用什么(ereg 或 preg)以及语法应该如何准确感到困惑。

注意:我不是在解析 HTML 或 XML,有时我会使用除 (例如 | 或 ~ 或 [tag] 或 ::) 以外的分隔符。我正在寻找一种通用方法来使用正则表达式在两个已知分隔符之间进行通配符替换,我不是在构建 HTML 或 XML 解析器。

我需要的是一个替换这个的正则表达式:

<sometag>everything in here</sometag>

用这个:

<sometag>new contents</sometag>

我已经在线阅读了一些文档,但我很困惑,我希望你们中的一位正则表达式专家可以提出一个简单的解决方案。我怀疑我会将值传递给函数,如下所示:

$new_text = swapText ( "<sometag>", $the_new_text_to_go_into_the_dag );

function swapText ( $in_tag_with_brackets_to_update, $in_new_text ) {
 // define tags
 $starting_tag  = $in_tag_with_brackets_to_update;
 $ending_tag    = str_replace( "<", "</", $in_tag_with_brackets_to_update) );

 // not sure if this is the proper regex match string or not
 // and/or if any escaping needs to be done on the tags
 $find_string         = "{$starting_tag}.*{$ending_tag}";
 $replace_with_string = "{$starting_tag}{$in_new_text}{$ending_tag}";

 // after some regex, this function should return new version of <tag>data</tag>
}

谢谢。

【问题讨论】:

  • 感谢 BalusC,但我并没有尝试解析 HTML,尽管我可以看到我的问题可能会让您相信这一点。
  • 你在做模板引擎吗?
  • Galen - 我只是在寻找一种方法来替换已知分隔符集中的未知文本块(我使用标签作为我将用作分隔符的众多事物之一的示例) .也许我应该为分隔符使用不同的示例。
  • 即使您的标签不是真正的 HTML 标签,如果它们始终遵循 HTML/XML 格式,解析器仍然是更好的选择。您应该能够轻松找到/替换 sometag 中的所有内容。

标签: php regex


【解决方案1】:

首先,如果您要替换的是 html,请使用 simple html dom 之类的内容。如果格式正是您所说的(例如,&lt;sometag&gt; 不能是 &lt;sometag &gt;),那么可以使用正则表达式。

不要使用基于 ereg 的函数,因为它们已被弃用,请使用 preg 函数。

preg_replace('%(<sometag>)[^<]*(</sometag>)%i', '$1something else$2', $str);

编辑
上面的一个稍微好一点的版本,现在支持在文本中使用&lt;

preg_replace('%(<sometag>).*?(</sometag>)%i', '$1something else$2', $str);

$1 和 $2 是括号之间的匹配文本。由于这些是常量,因此可以用常量替换它们

preg_replace('%<sometag>.*?</sometag>%i', '<sometag>something else</sometag>', $str);

【讨论】:

  • 结尾部分应该是 而不是 。我是否需要用 \ 转义 /(例如:)。 [^
  • 修复了结束标签。 [^
  • 这不起作用,&lt;/sometag&gt; 中的斜杠将被视为模式分隔符,导致解析错误。要么转义它,要么(更好)使用不同的模式分隔符。
  • 请说明出现-1的原因
  • -1 不是来自我,但如果开始和结束标记之间有换行符,您的解决方案将失败。
【解决方案2】:

您说您不会解析 xml,然后继续显示 xml 示例。这有点令人困惑。

现在,您不能使用正则表达式来解析 xml 的原因是它们不是上下文相关的。因此,有一整类问题不能使用正则表达式。这包括嵌套标签(无论它们是否为 xml),因此请记住这一点。

顺便说一句,您应该使用preg - 而不是eregereg 是一种使用较少、速度较慢且现已弃用的正则表达式类型。忘了它吧。

在 preg 使用的语言 pcre(Perl 兼容正则表达式)中,.(点)是一个通配符,它​​匹配任何单个字符(换行符除外)。您可以在匹配后放置一个量词。量词可以是明确的数字范围,例如 {1,3}(表示至少一个,但最多 3 个),或者您可以使用其中一种简写符号,例如 +{1,} 的缩写,意思是至少一个)或*(表示任何数字,包括零)。有了这些知识,你就可以用.*匹配任何东西。

默认情况下,表达式将匹配最大可能的模式(称为贪婪)。您可以使用 ? 修饰符来更改它。因此.*? 将匹配任何东西,但采用最短的模式。然后可以使用它来匹配任何分隔值,如下所示:

~<foo>.*?</foo>~

请注意,我在这里使用~ 作为分隔符,以避免在表达式中转义/。标准是使用/ 作为分隔符,在这种情况下,表达式将如下所示:

/<foo>.*?<\/foo>/

一般来说,上述做法是不好的做法,因为匹配否定字符类比匹配点要好得多,但是为了让事情对您来说简单,请忽略这一点,直到您了解基本知识为止。它会在大多数情况下工作。特别是,由于 . 不匹配换行符,如果内容包含换行符,这将不起作用。如果您需要此操作,您可以执行以下两项操作之一:将modifier 添加到表达式中,或者将. 替换为包含换行符的字符类。例如[\s\S](表示空白字符非空白字符,与任何内容相同)。这就是表达式的样子:

~<foo>.*?</foo>~s

或者:

~<foo>[\s\S]*?</foo>~

为了让这一切发挥作用,让我们将它传递给preg_replace 函数:

echo preg_replace('~<foo>.*?</foo>~s', '<foo>Lorem Ipsum</foo>', $input);

如果您的标记名称是可变的,您可以像使用 SQL 查询一样构建表达式。就像 SQL 一样,您需要转义某些字符。为此使用preg_quote

function swapText($tagname, $replacement_text, $input) {
  $tagname_escaped = preg_quote($tagname, '~');
  return preg_replace(
    '~<' . $tagname_escaped . '>.*?</' . $tagname_escaped . '>~s',
    '<' . $tagname . '>' . $replacement_text . '</' . $tagname . '>',
    $input);
}

【讨论】:

  • 请注意,. 匹配任何 除了 换行符。除此之外,很好的答案!
  • 谢谢。我认为它会满足我的需要,并且根据您的出色解释,我认为我可以重新使用 swapText 函数来处理我在整个应用程序中使用的其他类型的分隔符。再次感谢!
【解决方案3】:

@OP,如果您的任务非常简单,则无需使用复杂的正则表达式或解析器。仅使用普通子字符串的示例....

$mystr='<sometag>everything in here</sometag>';
$start=strpos($mystr,"<sometag>");
$end=strpos($mystr,"</sometag>");
print substr($mystr,0,$start+strlen("<sometag>") ) . "new value" . substr($mystr,$end);

【讨论】:

  • 谢谢 - 认为正则表达式可以工作,但你的工作得更好,而且还有正则表达式没有的换行符。
【解决方案4】:

我编写了以下函数来用通配符替换部分字符串:

function wildcardReplace($String,$Search,$Filler,$Wildcard = '???'){

        list($startStr,$endStr) = explode($Wildcard,$Search);

        $start = strpos($String,$startStr);

        // Make sure the end point is the first closest match after the start string.   

        $endofstarter = strpos($String,$startStr) + strlen($startStr);

        $startofender = strpos(
                    substr($String,$endofstarter),
                    $endStr
                ) + $endofstarter;


        $Result = substr($String,0,$start+strlen($startStr) ) . $Filler. substr($String,$startofender);

        // Replace any remaining stuff

        $RemainingString = substr($String,$startofender);

        // If theres any matches left, replace them

        if(strpos($RemainingString,$startStr)>-1) $Result = str_replace($RemainingString,wildcardReplace($RemainingString,$Search,$Filler),$Result);

        return $Result;
}

使用示例:$Output = wildcardReplace('&lt;a href="http://www.youtube.com/watch?v=dQw4w9WgXcQ"&gt;&lt;img src="rickroll.png" width="500"&gt;&lt;/a&gt;','width="???"',350,'???')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-24
    • 2011-09-09
    • 2015-08-24
    • 1970-01-01
    • 2014-01-26
    • 2015-05-07
    相关资源
    最近更新 更多