2009-04-16 8 views
29

Qualcuno ha un buon codice C# (ed espressioni regolari) che analizzerà una stringa e "collegherà" eventuali URL che potrebbero essere nella stringa?Codice C# per collegare gli URL in una stringa

+0

Questa sembra essere la domanda con la base di espressione canonica regolare soluzione. Forse qualcuno potrebbe modificare il titolo per aiutare gli utenti a trovarlo? – JasonSmith

risposta

42

E 'un compito abbastanza semplice si può acheive con Regex e un ready-to-go espressione regolare da:

Qualcosa di simile:

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

È potrebbe anche essere interessato non solo alla creazione di collegamenti, ma anche alla riduzione degli URL. Ecco un buon articolo su questo argomento:

Vedi anche:

+1

Ciao. Grande risposta. La maggior parte dei suggerimenti nel tuo post (e link) sembrano funzionare, ma sembrano tutti rompere tutti i link esistenti nel testo che viene valutato. –

+0

VSmith puoi provare diverse espressioni di reg da regixlib.com e trovare quale funziona meglio per te. –

+0

@VSmith: stai implicando che hai una stringa come "ciao there, vedi: http://www.b.com"; e vuoi solo collegare il secondo? –

4

Non è così facile come si può leggere in questo blog post by Jeff Atwood. È particolarmente difficile individuare dove finisce un URL.

Ad esempio, è la parte finale parentesi del URL o no:

  • http ​: //en.wikipedia.org/wiki/PCTools (CentralPointSoftware)
  • un URL tra parentesi (http ​: //en.wikipedia.org) altro testo

Nel primo caso, le parentesi fanno parte dell'URL. Nel secondo caso non lo sono!

+1

E come puoi vedere dagli URL in questa risposta, non tutti hanno capito bene :) – Ray

+0

Beh, in effetti, non volevo che i due URL venissero linkati. Ma sembra che questo non è supportato. – M4N

+0

La regex di Jeff sembra essere visualizzata male nel mio browser, credo che dovrebbe essere: "\ (? \ 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

Cheers per postare quello :) –

+0

Abbiamo finito per utilizzare qualcosa di molto simile, con una modifica. Abbiamo finito per assicurarci che la sostituzione avvenga solo una volta. Ciò significa che alla fine mancheranno alcuni collegamenti (collegamenti che si verificano più volte) ma rimuoverà la possibilità di collegamenti confusi in due casi: 1) Quando ci sono due collegamenti in cui uno è più dettagliato dell'altro. ad es. "http://google.com http://google.com/reader" 2) Quando c'è un mix di link HTML con collegamenti di testo normale. ad es. "Http://google.com Google" se (input.IndexOf (match.Value) == input.LastIndexOf (match.Value)) { ...} –

10

bene, dopo un sacco di ricerca su questo, e diversi tentativi di risolvere momenti in cui

  1. persone entrano in http://www.sitename.com e www.sitename.com nello stesso posto
  2. correzioni per parenthisis come (http://www.sitename.com) e http://msdn.microsoft.com/en-us/library/aa752574(vs.85).aspx
  3. gli URL lunghi come: 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

stiamo usando questa estensione HtmlHelper ...pensato che avrei condividere e ottenere eventuali commenti:

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

C'è classe:

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 
} 

utilizzarlo in questo modo:

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

Casi di test:

[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

Questo metodo funziona bene, comunque le partite su cose come o.context o altre stringhe che hanno un punto in esse. Sarebbe bello forzare .com/.org/.net etc, da qualche parte nella stringa –

+0

Inoltre costringe www, che non è sempre il caso. –

Problemi correlati