正则表达式通过链接替换单词

时间:2008-11-09 15:42:56

标签: regex string replace

我想写一个正则表达式,用链接代替单词Paris,因为只有单词没有准备好作为链接的一部分。

示例:

    i'm living <a href="Paris" atl="Paris link">in Paris</a>, near Paris <a href="gare">Gare du Nord</a>,  i love Paris.

会变成

    i'm living.........near <a href="">Paris</a>..........i love <a href="">Paris</a>.

7 个答案:

答案 0 :(得分:6)

这很难一步到位。编写单个正则表达式实际上是不可能的。

尝试两步法。

  1. 在每个“巴黎”周围放置一个链接,无论是否已存在其他链接。
  2. 查找所有错误嵌套的链接(<a href="..."><a href="...">Paris</a></a>),并删除内部链接。
  3. 第一步的正则表达式很简单:

    \bParis\b
    

    第二步的正则表达式稍微复杂一些:

    (<a[^>]+>.*?(?!:</a>))<a[^>]+>(Paris)</a>
    

    在整个字符串中使用该字符串并将其替换为匹配组1和2的内容,从而有效地删除多余的内部链接。

    用简单的词语解释正则表达式#2:

    • 查找每个链接(<a[^>]+>),可选地后跟任何本身不是后跟关闭链接(.*?(?!:</a>))的链接。将其保存到匹配组1中。
    • 现在查找下一个链接(<a[^>]+>)。确保它在那里,但不要保存它。
    • 现在寻找巴黎这个词。将其保存到匹配组2中。
    • 寻找结束链接(</a>)。确保它在那里,但不要保存它。
    • 用第1组和第2组的内容替换所有内容,从而丢失您未保存的所有内容。

    该方法假设了这些方面的条件:

    • 您的输入HTML并没有被严重破坏。
    • 你的正则表达式支持非贪婪量词(。*?)和零宽度负前瞻断言((?!:...))。
    • 您只在步骤1的链接中包含“巴黎”一词,不包含其他字符。每个“Paris”变为“<a href"...">Paris</a>”,否则第二步将失败(直到您更改第二个正则表达式)。
    • BTW:正则表达式#2明确允许这样的结构:

      <a href="">in the <b>capital of France</b>, <a href="">Paris</a></a>

      剩余链接来自第一步,第二步的替换结果将是:

      <a href="">in the <b>capital of France</b>, Paris</a>

答案 1 :(得分:4)

您可以搜索此正则表达式:

(<a[^>]*>.*?</a>)|Paris

这个正则表达式匹配一个链接,它捕获到第一个(也是唯一的)捕获组,或者单词Paris。

仅当捕获组与任何内容不匹配时,才将匹配替换为您的链接。

E.g。在C#中:

resultString = 
    Regex.Replace(
        subjectString, 
        "(<a[^>]*>.*?</a>)|Paris", 
        new MatchEvaluator(ComputeReplacement));

public String ComputeReplacement(Match m) {
    if (m.groups(1).Success) {
        return m.groups(1).Value;
    } else {
        return "<a href=\"link to paris\">Paris</a>";
    }
}

答案 2 :(得分:3)

这个问题的传统答案:使用真正的HTML解析器。因为RE并不擅长在上下文中运行。 HTML很复杂,'a'标签可以有或没有属性,无论如何,链接中都可以包含HTML等等。

答案 3 :(得分:0)

正则表达式:

!(<a.*</a>.*)*Paris!isU

<强>替换

$1<a href="Paris">Paris</a>

$ 1引用第一个子模式(至少在PHP中)。根据您使用的语言,它可能会略有不同。

这应该用替换中的链接替换“Paris”的所有出现。它只是检查在“巴黎”之前是否所有打开的标签都已关闭。

PHP示例:

<?php
$s = 'i\'m living <a href="Paris" atl="Paris link">in Paris</a>, near Paris <a href="gare">Gare du Nord</a>, i love Paris.'; 
$regex = '!(<a.*</a>.*)*Paris!isU'; 
$replace = '$1<a href="Paris">Paris</a>'; 
$result = preg_replace( $regex, $replace, $s); 
?>

增加:

这不是最好的解决方案。这种正则表达式不起作用的一种情况是你有一个img-Tag,它不在a-Element中。当您将该图像的title-Attribute设置为“Paris”时,此“Paris”也将被替换。这不是你想要的。 然而,我认为没有办法用一个简单的正则表达式完全解决你的问题。

答案 4 :(得分:0)

如果您在这种情况下不限于使用正则表达式,那么XSLT是您可以定义此替换的语言的不错选择,因为它“理解”XML。

您定义了两个模板: 一个模板查找链接并删除那些没有“Paris”作为正文文本的链接。另一个模板找到其他所有内容,将其拆分为单词并添加标签。

答案 5 :(得分:0)

  $pattern = 'Paris';
  $text = 'i\'m living <a href="Paris" atl="Paris link">in Paris</a>,  near Paris <a href="gare">Gare du Nord</a>,  i love Paris.';

  // 1. Define 2 arrays:
  //  $matches[1] - array of links with our keyword
  //  $matches[2] - array of keyword
  preg_match_all('@(<a[^>]*?>[^<]*?'.$pattern.'[^<]*?</a>)|(?<!\pL)('.$pattern.')(?!\pL)@', $text, $matches);

  // Exists keywords for replace? Define first keyword without tag <a>
  $number = array_search($pattern, $matches[2]);

  // Keyword exists, let's go rock
  if ($number !== FALSE) {

    // Replace all link with temporary value
    foreach ($matches[1] as $k => $tag) {
      $text = preg_replace('@(<a[^>]*?>[^<]*?'.$pattern.'[^<]*?</a>)@', 'KEYWORD_IS_ALREADY_LINK_'.$k, $text, 1);
    }

    // Replace our keywords with link
    $text = preg_replace('/(?<!\pL)('.$pattern.')(?!\pL)/', '<a href="">'.$pattern.'</a>', $text);

    // Return link
    foreach ($matches[1] as $k => $tag) {

      $text = str_replace('KEYWORD_IS_ALREADY_LINK_'.$k, $tag, $text);
    }

    // It's work!
    echo $text;
  }

答案 6 :(得分:-2)

正则表达不替换。语言有。

语言和库也会从包含您关注的单词列表的数据库或文件中读取,并将URL与其名称相关联。这是我能想象的最简单的替换,我的单个正则表达式(perl用于替换语法。)

s/([a-z-']+)/<a href="http:\/\/en.wikipedia.org\/wiki\/$1">$1<\/a>/i

正确的名称可能会更好:

s/([A-Z][a-z-']+)/<a href="http:\/\/en.wikipedia.org\/wiki\/$1">$1<\/a>/gi;

当然“Baton Rouge”将成为两个链接:

<a href="http://en.wikipedia.org/wiki/Baton">Baton</a> 
<a href="http://en.wikipedia.org/wiki/Rouge">Rouge</a>

Perl 中,您可以这样做:

my $barred_list_of_cities 
    = join( '|'
    , sort { ( length $a <=> $b ) || ( $a cmp $b ) } keys %url_for_city_of
    );
s/($barred_list_of_cities)/<a href="$url_for_city_of{$1}">$1<\/a>/g;

但同样,它是一个语言,它为正则表达式实现了一组操作,正则表达式不做任何事情。 (实际上,它是一个如此常见的应用程序,如果某个地方没有CPAN模块,我会感到惊讶,你只需要加载哈希值。