2008-11-13 3 views
20

Ich habe eine Last von Benutzer eingereichten Inhalten. Es ist HTML und kann URLs enthalten. Einige von ihnen werden bereits <a> sein (wenn der Benutzer gut ist), aber manchmal sind die Benutzer faul und geben einfach www.something.com oder bestenfalls http://www.something.com ein.Brauchen Sie eine gute Regex, um URLs in Links zu konvertieren, aber bestehende Links in Ruhe lassen

Ich kann keinen ordentlichen Regex finden, um URLs zu erfassen, aber ignorieren Sie solche, die sich unmittelbar rechts von einem doppelten Anführungszeichen oder '>' befinden. Jeder hat einen?

Antwort

14

Jan Goyvaerts, Schöpfer von RegexBuddy, hat written a response zu Jeff Atwood's Blog, der die Probleme anspricht, die Jeff hatte und bietet eine nette Lösung.

\b(?:(?:https?|ftp|file)://|www\.|ftp\.)[-A-Z0-9+&@#/%=~_|$?!:,.]*[A-Z0-9+&@#/%=~_|$] 

Um Spiele zu ignorieren, die sich direkt neben einem "oder> auftreten, Sie (?<![">]) zu Beginn der regex könnte hinzufügen, so erhalten Sie

(?<![">])\b(?:(?:https?|ftp|file)://|www\.|ftp\.)[-A-Z0-9+&@#/%=~_|$?!:,.]*[A-Z0-9+&@#/%=~_|$] 

Dies wird in voller Adressen übereinstimmen (http://.. .) und Adressen, die mit www oder ftp beginnen - du bist kein Glück mit Adressen wie ars.userfriendly.org ...

0

Shameless Stecker:.. Sie können hier (regular expression replace a word by a link) für Inspiration

.

Die Frage wurde gestellt, um ein Wort durch einen bestimmten Link zu ersetzen, es sei denn, es gab bereits einen Link. Das Problem, das Sie haben, ist mehr oder weniger dasselbe.

Alles, was Sie brauchen, ist eine Regex, die eine URL (anstelle des Wortes) entspricht. Die einfachste Annahme wäre wie folgt: Eine URL (optional) beginnt mit "http://", oder "mailto:" und dauert so lange, wie es keine Leerzeichen, Zeilenumbrüche, Tag-Klammern oder Anführungszeichen gibt.

Vorsicht, lange Regex voraus. Wenden Sie Groß- und Kleinschreibung an.

(href\s*=\s*['"]?)?((?:http://|ftp://|mailto:)?[^.,<>"'\s\r\n\t]+(?:\.(?![.<>"'\s\r\n])[^.,!<>"'\s\r\n\t]+)+) 

Seien Sie gewarnt - dies wird auch URLs übereinstimmen, die technisch ungültig sind, und es wird things.formatted.like.this als URL erkennen. Es hängt von Ihren Daten ab, ob es zu unempfindlich ist. Ich kann die Regex genau abstimmen, wenn Sie Beispiele haben, in denen sie falsch positive Ergebnisse zurückgibt.

Der Regex erzeugt zwei Match-Gruppen. Gruppe 2 enthält das übereinstimmende Objekt, bei dem es sich höchstwahrscheinlich um eine URL handelt. Gruppe 1 enthält entweder eine leere Zeichenfolge oder eine 'href="'. Sie können es als Indikator dafür verwenden, dass diese Übereinstimmung innerhalb ein href -Parameter einer vorhandenen Verbindung aufgetreten ist, und Sie müssen diese nicht berühren.

Sobald Sie bestätigen, dass dies das richtige für Sie tut die meiste Zeit (mit Benutzer bereitgestellten Daten, können Sie nie sicher sein), können Sie den Rest in zwei Schritten tun, wie ich es in der anderen vorgeschlagen Frage:

  1. Machen Sie einen Link an jeder URL ist (es sei denn, gibt es etwas in Übereinstimmung Gruppe 1!) Diese wird produzieren doppelt verschachtelten <a> Tags für Dinge, die einen Link bereits haben.
  2. Scan für <a> Tags falsch verschachtelt, überspringen die innerste
0

Beseitigung bestehender verwenden diejenigen, nur einen Blick hinter - (?<!href=") zu Beginn des regulären Ausdrucks hinzuzufügen, so dass es in etwa so aussehen würde:

/(?<!href=")http://\S*/ 

Natürlich ist dies nicht eine komplette Lösung für finden alle Arten von URLs, aber dies sollte Ihr Problem der Unordnung mit bestehenden lösen.

10

Ich machte eine leichte Modifikation der Regex in der ursprünglichen Antwort enthalten:

(?<![.*">])\b(?:(?:https?|ftp|file)://|[a-z]\.)[-A-Z0-9+&#/%=~_|$?!:,.]*[A-Z0-9+&#/%=~_|$] 

, die für mehr Subdomains erlaubt, und läuft auch eine vollständige Kontrolle über Tags. Um dies auf PHP-Preg ersetzen, können Sie verwenden:

$convertedText = preg_replace('@(?<![.*">])\b(?:(?:https?|ftp|file)://|[a-z]\.)[-A-Z0-9+&#/%=~_|$?!:,.]*[A-Z0-9+&#/%=~_|$]@i', '<a href="\0" target="_blank">\0</a>', $originalText); 

Hinweis, entfernte ich @ aus der Regex, um sie als Trennzeichen für preg_replace zu verwenden. Es ist ziemlich selten, dass @ in einer URL trotzdem verwendet wird.

Natürlich können Sie den Ersatztext, modifizieren und Ziel entfernen = "_ blank" oder rel = "nofollow" usw.

Hoffnung hinzufügen, das hilft.

+0

Ich habe ein = zum (? ]) zu Beginn hinzugefügt nicht link (nicht kotierte Anker-Tags) zu brechen. Nice regex btw :) – Joel

+0

@Joel: Sind Sie sicher, dass Sie wollen, dass Lookbehind bedeutet "Stellen Sie sicher, dass es unmöglich ist, einen Punkt, ein Sternchen, ein Zitat oder eine schließende Klammer vor der aktuellen Position in der Zeichenfolge"? –

11

Dieser Thread ist alt wie die Hügel, aber ich stieß auf es bei der Arbeit an meinem eigenen Problem: Das heißt, konvertieren Sie alle URLs in Links, aber lassen Sie alle, die bereits in Anker-Tags sind. Nach einer Weile ist es das, was herausgesprungen ist:

(?!(?!.*?<a)[^<]*<\/a>)(?:(?:https?|ftp|file)://|www\.|ftp\.)[-A-Z0-9+&#/%=~_|$?!:,.]*[A-Z0-9+&#/%=~_|$] 

mit folgenden Eingabe:

<a href="http://www.google.com" rel="nofollow">http://www.google.com</a> 
<a href="http://google.com" rel="nofollow">http://google.com</a> 
<a href="www.google.com" rel="nofollow">www.google.com</a> 

<p><a href="http://www.google.com" rel="nofollow">http://www.google.com</a><p> 

this is a normal sentence. let's hope it's ok. 

<a href="http://www.google.com">www.google.com</a> 

Nur beitragen wollten zurück:

http://www.google.com 
http://google.com 
www.google.com 

<p>http://www.google.com<p> 

this is a normal sentence. let's hope it's ok. 

<a href="http://www.google.com">www.google.com</a> 

die Ausgabe eines preg_replace Dies ist um jemanden Zeit zu sparen.

+5

Das hat bei mir funktioniert. Du bist ein Champion! Hinzugefügt das "i" -Flag und dies ist der resultierende php: '$ text = preg_replace ('@ (?! (?!. *?? ) (?: (?: Https? | Ftp | Datei): // | www \. | ftp \.) [- A-Z0-9 + & # /% = ~ _ | $?!:,.] * [A-Z0-9 + & # /% = ~ _ | $] @ i ',' \0 ', $ text); 'die anderen oben genannten Lösungen funktionierten nicht in jedem Fall für mich. – dtbaker

1
if (preg_match('/\b(?<!=")(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[A-Z0-9+&@#\/%=~_|](?!.*".*>)(?!.*<\/a>)/i', $subject)) { 
    # Successful match 
} else { 
    # Match attempt failed 
}