2009-04-16 9 views
29

¿Alguien tiene algún buen código C# (y expresiones regulares) que analizará una cadena y "enlazará" las URL que puedan estar en la cadena?Código de C# para vincular URL en una cadena

+0

Esta parece ser la pregunta con la canónica de expresiones regulares de base solución. Tal vez alguien podría editar el título para ayudar a los buscadores a encontrarlo. – JasonSmith

Respuesta

42

Es una tarea bastante simple que puede acheive con Regex y una lista para ir expresión regular a partir de:

Algo así como:

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>"); 

Usted también puede estar interesado no solo en crear enlaces sino también en acortar URL. Aquí es un buen artículo sobre este tema:

Véase también:

+1

Hola. Gran respuesta. La mayoría de las sugerencias en su publicación (y enlaces) parecen funcionar, pero todas parecen romper cualquier enlace existente en el texto que se evalúa. –

+0

VSmith puede probar diferentes expresiones de reg de regixlib.com y encontrar cuál funciona mejor para usted. –

+0

@VSmith: ¿Quiere dar a entender que tiene una cadena como "hola there, consulte: http://www.b.com"; y solo quieres vincular el segundo? –

4

No es tan fácil como se puede leer en este blog post by Jeff Atwood. Es especialmente difícil detectar dónde termina una URL.

Por ejemplo, es la parte trasera paréntesis de la URL o no:

  • http ​: //en.wikipedia.org/wiki/PCTools (CentralPointSoftware)
  • una URL entre paréntesis (http ​: //en.wikipedia.org) más texto

En el primer caso, los paréntesis son parte de la URL. En el segundo caso, ¡no lo son!

+1

Y como puede ver en las URL enlazadas en esta respuesta, no todo el mundo lo hace bien :) – Ray

+0

Bueno, de hecho, no quería que las dos URL se vinculen. Pero parece que esto no es compatible. – M4N

+0

La expresión regular de Jeff parece mostrar mal en mi navegador, creo que debería ser: "\ (? \ Bhttp: // [-A-Za-z0-9 + & @ # /%? = ~ _() | !: ,.;] * [- A-Za-z0-9 + & @ # /% = ~ _() |] " –

6
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; 
} 
+0

Saludos por publicarlo :) –

+0

Terminamos usando algo muy similar, con una modificación. Terminamos asegurándonos de que el reemplazo solo ocurra una vez. Esto significa que terminaremos perdiendo algunos enlaces (enlaces que ocurren más de una vez) pero elimina la posibilidad de enlaces ilegibles en dos casos: 1) Cuando hay dos enlaces donde uno es más detallado que el otro. p. "http://google.com http://google.com/reader" 2) Cuando hay una combinación de enlaces HTML con enlaces de texto sin formato. p. "Http://google.com Google" si (input.IndexOf (match.Value) == input.LastIndexOf (match.Value)) { ... } –

10

así, después de un gran trabajo de investigación sobre este tema, y ​​varios intentos de solucionar momentos en

  1. personas entran en http://www.sitename.com y www.sitename.com en el mismo puesto
  2. correcciones para parenthisis como (http://www.sitename.com) y http://msdn.microsoft.com/en-us/library/aa752574(vs.85).aspx
  3. URL largas como: 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

ahora estamos usando esta extensión HtmlHelper ...pensé que iba a compartir y obtener cualquier comentario:

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; 
    } 
1

hay clase:

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 
} 

lo uso en este método:

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; 
} 

casos de prueba:

[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()); 
} 
+0

Esto funciona bien, sin embargo, coincide con el cosas como o.context u otra cadena que tenga un punto en ellas. Sería bueno forzar .com/.org/.net, etc, en algún lugar de la cadena –

+0

También obliga a www, que no siempre es el caso. –

Cuestiones relacionadas