C#代码:将字符串中的URL转换为链接

33

有没有好的C#代码(和正则表达式),可以解析一个字符串并将其中可能存在的URL链接化?


1
这似乎是使用规范正则表达式解决方案的问题。也许有人可以编辑标题以帮助搜索者找到它? - JasonSmith
7个回答

47

这是一个非常简单的任务,您可以使用Regex和来自以下网站的现成正则表达式来完成:

类似于:

var html = Regex.Replace(html, @"^(http|https|ftp)\://[a-zA-Z0-9\-\.]+" +
                         "\.[a-zA-Z]{2,3}(:[a-zA-Z0-9]*)?/?" +
                         "([a-zA-Z0-9\-\._\?\,\'/\\\+&%\$#\=~])*$",
                         "<a href=\"$1\">$1</a>");

除了创建链接外,您可能也对缩短URL感兴趣。这是一篇关于这个主题的好文章:

另请参见:


2
嗨。回复很棒。你帖子中的大部分建议(和链接)似乎都可以使用,但它们似乎会破坏正在评估的文本中的任何现有链接。 - BeYourOwnGod
1
VSmith,你可以尝试从regixlib.com上获取不同的正则表达式,并找到最适合你的那一个。 - Konstantin Tarkus
2
嗯,那很好地运作了。从而证明了我们在这里所提出的所有观点 ;) - Zhaph - Ben Duguid
10
很棒!但是,要使用 .Net Regex(System.Text.RegularExpressions.Regex)并使用在文本行中的网址,我需要修改代码如下:Regex.Replace(answer, @"((http|https|ftp)://[a-zA-Z0-9-.]+.[a-zA-Z]{2,3}(:[a-zA-Z0-9])?/?([a-zA-Z0-9-._?,'/\+&%$#=~]))", @"<a href='$1'>$1</a>")。 - Marcel
3
我使用以下正则表达式来处理不完全限定的主机名:@"((http|https|ftp)\://[a-zA-Z0-9\-\.]+(\.[a-zA-Z]{2,3})?(:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\-\._\?\,\'/\\\+&amp;%\$#\=~])*)" - cubetwo1729
显示剩余3条评论

14

在大量研究和几次尝试修复以下问题后:

  1. 用户在同一篇文章中输入http://www.sitename.com和www.sitename.com的情况;
  2. 修复圆括号()中的链接,如(http://www.sitename.com) 和 http://msdn.microsoft.com/en-us/library/aa752574(vs.85).aspx
  3. 处理长网址,如:http://www.amazon.com/gp/product/b000ads62g/ref=s9_simz_gw_s3_p74_t1?pf_rd_m=atvpdkikx0der&pf_rd_s=center-2&pf_rd_r=04eezfszazqzs8xfm9yd&pf_rd_t=101&pf_rd_p=470938631&pf_rd_i=507846

我们现在使用这个HtmlHelper扩展...我想分享并获得任何建议:

    private static Regex regExHttpLinks = new Regex(@"(?<=\()\b(https?://|www\.)[-A-Za-z0-9+&@#/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#/%=~_()|](?=\))|(?<=(?<wrap>[=~|_#]))\b(https?://|www\.)[-A-Za-z0-9+&@#/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#/%=~_()|](?=\k<wrap>)|\b(https?://|www\.)[-A-Za-z0-9+&@#/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#/%=~_()|]", RegexOptions.Compiled | RegexOptions.IgnoreCase);

    public static string Format(this HtmlHelper htmlHelper, string html)
    {
        if (string.IsNullOrEmpty(html))
        {
            return html;
        }

        html = htmlHelper.Encode(html);
        html = html.Replace(Environment.NewLine, "<br />");

        // replace periods on numeric values that appear to be valid domain names
        var periodReplacement = "[[[replace:period]]]";
        html = Regex.Replace(html, @"(?<=\d)\.(?=\d)", periodReplacement);

        // create links for matches
        var linkMatches = regExHttpLinks.Matches(html);
        for (int i = 0; i < linkMatches.Count; i++)
        {
            var temp = linkMatches[i].ToString();

            if (!temp.Contains("://"))
            {
                temp = "http://" + temp;
            }

            html = html.Replace(linkMatches[i].ToString(), String.Format("<a href=\"{0}\" title=\"{0}\">{1}</a>", temp.Replace(".", periodReplacement).ToLower(), linkMatches[i].ToString().Replace(".", periodReplacement)));
        }

        // Clear out period replacement
        html = html.Replace(periodReplacement, ".");

        return html;
    }

8
protected string Linkify( string SearchText ) {
    // this will find links like:
    // http://www.mysite.com
    // as well as any links with other characters directly in front of it like:
    // href="http://www.mysite.com"
    // you can then use your own logic to determine which links to linkify
    Regex regx = new Regex( @"\b(((\S+)?)(@|mailto\:|(news|(ht|f)tp(s?))\://)\S+)\b", RegexOptions.IgnoreCase );
    SearchText = SearchText.Replace( "&nbsp;", " " );
    MatchCollection matches = regx.Matches( SearchText );

    foreach ( Match match in matches ) {
        if ( match.Value.StartsWith( "http" ) ) { // if it starts with anything else then dont linkify -- may already be linked!
            SearchText = SearchText.Replace( match.Value, "<a href='" + match.Value + "'>" + match.Value + "</a>" );
        }
    }

    return SearchText;
}

感谢您发布这个 :) - Zhaph - Ben Duguid
我们最终使用了非常相似的东西,只有一个修改。我们最终确保替换仅发生一次。这意味着我们将错过一些链接(出现多次的链接),但消除了两种情况下混乱链接的可能性: 1)当存在两个链接其中一个比另一个更详细时。 例如:“http://google.com http://google.com/reader” 2)当HTML链接与纯文本链接混合时。 例如:“http://google.com <a href="http://google.com">Google</a>”if (input.IndexOf(match.Value) == input.LastIndexOf(match.Value)) { ... } - Michael Krauklis

4
这并不像你在Jeff Atwood的博客文章中所读到的那样容易。特别是很难检测URL的结尾。
例如,括号是否属于URL的一部分:
  • http​://en.wikipedia.org/wiki/PCTools(CentralPointSoftware)
  • 带括号的URL (http​://en.wikipedia.org) 更多文本
在第一个案例中,括号是URL的一部分。在第二个案例中,它们不是!

1
正如您可以从此答案中链接的URL看到,不是每个人都做得正确 :) - Ray
实际上,我并不想让这两个URL链接化。但似乎这不被支持。 - M4N
Jeff的正则表达式在我的浏览器中显示不好,我认为应该是:"(?\bhttp://[-A-Za-z0-9+&@#/%?=_()|!:,.;]*[-A-Za-z0-9+&@#/%=_()|]" - Zhaph - Ben Duguid

1

1

有一个类:

public class TextLink
{
    #region Properties

    public const string BeginPattern = "((http|https)://)?(www.)?";

    public const string MiddlePattern = @"([a-z0-9\-]*\.)+[a-z]+(:[0-9]+)?";

    public const string EndPattern = @"(/\S*)?";

    public static string Pattern { get { return BeginPattern + MiddlePattern + EndPattern; } }

    public static string ExactPattern { get { return string.Format("^{0}$", Pattern); } }

    public string OriginalInput { get; private set; }

    public bool Valid { get; private set; }

    private bool _isHttps;

    private string _readyLink;

    #endregion

    #region Constructor

    public TextLink(string input)
    {
        this.OriginalInput = input;

        var text = Regex.Replace(input, @"(^\s)|(\s$)", "", RegexOptions.IgnoreCase);

        Valid = Regex.IsMatch(text, ExactPattern);

        if (Valid)
        {
            _isHttps = Regex.IsMatch(text, "^https:", RegexOptions.IgnoreCase);
            // clear begin:
            _readyLink = Regex.Replace(text, BeginPattern, "", RegexOptions.IgnoreCase);
            // HTTPS
            if (_isHttps)
            {
                _readyLink = "https://www." + _readyLink;
            }
            // Default
            else
            {
                _readyLink = "http://www." + _readyLink;
            }
        }
    }

    #endregion

    #region Methods

    public override string ToString()
    {
        return _readyLink;
    }

    #endregion
}

在这个方法中使用它:

public static string ReplaceUrls(string input)
{
    var result = Regex.Replace(input.ToSafeString(), TextLink.Pattern, match =>
    {
        var textLink = new TextLink(match.Value);
        return textLink.Valid ?
            string.Format("<a href=\"{0}\" target=\"_blank\">{1}</a>", textLink, textLink.OriginalInput) :
            textLink.OriginalInput;
    });
    return result;
}

测试用例:

[TestMethod]
public void RegexUtil_TextLink_Parsing()
{
    Assert.IsTrue(new TextLink("smthing.com").Valid);
    Assert.IsTrue(new TextLink("www.smthing.com/").Valid);
    Assert.IsTrue(new TextLink("http://smthing.com").Valid);
    Assert.IsTrue(new TextLink("http://www.smthing.com").Valid);
    Assert.IsTrue(new TextLink("http://www.smthing.com/").Valid);
    Assert.IsTrue(new TextLink("http://www.smthing.com/publisher").Valid);

    // port
    Assert.IsTrue(new TextLink("http://www.smthing.com:80").Valid);
    Assert.IsTrue(new TextLink("http://www.smthing.com:80/").Valid);
    // https
    Assert.IsTrue(new TextLink("https://smthing.com").Valid);

    Assert.IsFalse(new TextLink("").Valid);
    Assert.IsFalse(new TextLink("smthing.com.").Valid);
    Assert.IsFalse(new TextLink("smthing.com-").Valid);
}

[TestMethod]
public void RegexUtil_TextLink_ToString()
{
    // default
    Assert.AreEqual("http://www.smthing.com", new TextLink("smthing.com").ToString());
    Assert.AreEqual("http://www.smthing.com", new TextLink("http://www.smthing.com").ToString());
    Assert.AreEqual("http://www.smthing.com/", new TextLink("smthing.com/").ToString());

    Assert.AreEqual("https://www.smthing.com", new TextLink("https://www.smthing.com").ToString());
}

这个功能很好,但是它匹配像o.context这样的字符串,或者其他带有句点的字符串。希望能够在字符串中强制包含.com/.org/.net等内容。 - Todd Horst
同时它强制添加了“www”,而这并不总是必要的。 - Todd Horst

0
这对我有效:
str = Regex.Replace(str,
                @"((http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?)",
                "<a target='_blank' href='$1'>$1</a>");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接