【问题标题】:Regex string issue in making plain text urls clickable使纯文本网址可点击的正则表达式字符串问题
【发布时间】:2012-02-08 15:41:47
【问题描述】:

我需要一个 C# 中的工作正则表达式代码,它可以检测字符串中的纯文本 url (http/https/ftp/ftps),​​并通过在其周围放置一个具有相同 url 的锚标记来使它们可点击。我已经做了一个Regex模式,代码附在下面。

但是,如果输入字符串中已经存在任何可点击的 url,那么上面的代码会在其上放置另一个锚标记。例如,以下代码中的现有子字符串: string sContent: "ftp://www.abc.com'>ftp://www.abc.com" 在运行以下代码时有另一个锚标记。有什么办法可以解决吗?

        string sContent = "ttt <a href='ftp://www.abc.com'>ftp://www.abc.com</a> abc ftp://www.abc.com abbbbb http://www.abc2.com";

        Regex regx = new Regex("(http|https|ftp|ftps)://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", RegexOptions.IgnoreCase);

        MatchCollection mactches = regx.Matches(sContent);

        foreach (Match match in mactches)
        {
            sContent = sContent.Replace(match.Value, "<a href='" + match.Value + "'>" + match.Value + "</a>");
        }

另外,我想要一个正则表达式代码,使电子邮件可以点击“mailto”标签。我可以自己做,但是上面提到的双锚标签的问题也会出现在里面。

【问题讨论】:

    标签: c# .net regex url


    【解决方案1】:

    查看:Detect email in text using regexRegex URL Replace, ignore Images and existing Links,只需替换链接的正则表达式,它永远不会替换标签内的链接,只会替换内容。

    http://html-agility-pack.net/?z=codeplex

    类似:


    string textToBeLinkified = "... your text here ...";
    const string regex = @"((www\.|(http|https|ftp|news|file)+\:\/\/)[_.a-z0-9-]+\.[a-z0-9\/_:@=.+?,##%&amp;~-]*[^.|\'|\# |!|\(|?|,| |>|<|;|\)])";
    Regex urlExpression = new Regex(regex, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
    
    HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
    doc.LoadHtml(textToBeLinkified);
    
    var nodes = doc.DocumentNode.SelectNodes("//text()[not(ancestor::a)]") ?? new HtmlNodeCollection();
    foreach (var node in nodes)
    {
        node.InnerHtml = urlExpression.Replace(node.InnerHtml, @"<a href=""$0"">$0</a>");
    }
    string linkifiedText = doc.DocumentNode.OuterHtml;
    

    【讨论】:

      【解决方案2】:

      我知道我来晚了,但是现有答案没有解决正则表达式的几个问题。首先也是最烦人的,是反斜杠的森林。如果您使用 C# 的逐字字符串,则不必进行所有双重转义。而且无论如何,一开始就不需要大多数反斜杠。

      其次,有一点:([\\w+?\\.\\w+])+。方括号形成一个字符类,其中的所有内容都被视为文字字符或类简写,如\w。但是去掉方括号并不足以让它发挥作用。我怀疑这就是你想要的:\w+(?:\.\w+)+

      第三,正则表达式末尾的量词 - ]*)? - 不匹配。 * 可以匹配零个或多个字符,因此没有必要将封闭组设为可选。此外,这种安排可能导致严重的性能下降。详情请见this page

      还有其他一些小问题,但我现在不会讨论它们。这是新的和改进的正则表达式:

      @"(?n)(https?|ftps?)://\w+(\.\w+)+([-a-zA-Z0-9~!@#$%^&*()_=+/?.:;',\\]*)(?![^<>]*+(>|</a>))"
      

      否定的前瞻 - (?![^&lt;&gt;]*+(&gt;|&lt;/a&gt;)) 是阻止标签内或锚元素内容中的匹配。不过,它仍然非常粗糙。有几个区域,例如 &lt;script&gt; 元素内部,您不希望它匹配但它确实匹配。但试图涵盖所有可能性会导致一英里长的正则表达式。

      【讨论】:

        【解决方案3】:

        我在您的示例测试字符串中注意到,如果重复链接,例如ftp://www.abc.com 在字符串中并且已经链接,那么结果将是双锚链接。您已经拥有和 @stema 提供的正则表达式将起作用,但您需要以不同的方式替换 sContent 变量中的匹配项。

        以下代码示例应该可以满足您的需求:

        string sContent = "ttt <a href='ftp://www.abc.com'>ftp://www.abc.com</a> abc ftp://www.abc.com abbbbb http://www.abc2.com";
        
        Regex regx = new Regex("(?<!(?:href='|<a[^>]*>))(http|https|ftp|ftps)://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", RegexOptions.IgnoreCase);
        
        MatchCollection matches = regx.Matches(sContent);
        
        for (int i = matches.Count - 1; i >= 0 ; i--)
        {
            string newURL = "<a href='" + matches[i].Value + "'>" + matches[i].Value + "</a>";
        
           sContent = sContent.Remove(matches[i].Index, matches[i].Length).Insert(matches[i].Index, newURL);
        }
        

        【讨论】:

          【解决方案4】:

          试试这个

          Regex regx = new Regex("(?<!(?:href='|>))(http|https|ftp|ftps)://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", RegexOptions.IgnoreCase);
          

          它应该适用于您的示例。

          (?&lt;!(?:href='|&gt;)) 是一个否定的lookbehind,这意味着模式只有在它前面没有“href='”或“>”时才匹配。

          regular-expressions.info上查看环视

          尤其是zero-width negative lookbehind assertion on msdn

          看到一些东西similar on Regexr。我不得不从后面删除交替,但 .net 应该能够处理它。

          更新

          为了确保也有(可能)正确处理“&lt;p&gt;ftp://www.def.com&lt;/p&gt;”之类的情况,我改进了正则表达式

          Regex regx = new Regex("(?<!(?:href='|<a[^>]*>))(http|https|ftp|ftps)://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", RegexOptions.IgnoreCase);
          

          lookbehind (?&lt;!(?:href='|&lt;a[^&gt;]*&gt;)) 现在正在检查是否没有“href='”或以“开头的标签”

          测试字符串的输出

          ttt <a href='ftp://www.abc.com'>ftp://www.abc.com</a> abc <p>ftp://www.def.com</p> abbbbb http://www.ghi.com
          

          用这个表达方式

          ttt <a href='ftp://www.abc.com'>ftp://www.abc.com</a> abc <p><a href='ftp://www.def.com'>ftp://www.def.com</a></p> abbbbb <a href='http://www.ghi.com'>http://www.ghi.com</a>
          

          【讨论】:

          • 后面找">"有什么意义?解析失败

            这是stackoverflow.com</p>

          • @IgorKorkhov 在 OPs 示例中会错误匹配&lt;a href='ftp://www.abc.com'&gt;ftp://www.abc.com&lt;/a&gt; 中的链接描述。
          • 我确实理解 OP 的问题(以及仅使用正则表达式无法解决的事实 - HTML 不是常规语言)。但我向您展示了一个有效的片段,其中纯文本链接必须包含在锚点中,但不会。
          • @IgorKorkhov 我不知道这是否是 OP 的有效片段,我假设他没有处理 html 文件。但我不知道。
          • @IgorKorkhov 我更新了我的答案,它现在处理你的例子。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多