2009-06-10 22 views
2

Ich bin ein relativ newb, wenn es um reguläre Ausdrücke kommt, aber ich fange an den Dreh raus zu bekommen. Ich begann eine Methode in Java schreiben eine Zeichenfolge „Linkify“ - das heißt, scannen sie für alle Verweise von URLs (dh „http: // ...“) oder Strings, die wie Web-Adressen ("www aussehen . example.com ... ")Conditional Ersatz mit regex

So zum Beispiel, wenn ich einen String hatte, die wie folgt aussah:

My favorite site is http://www.example.com. What is yours? 

Nachdem es durch das Verfahren lief, können Sie eine Zeichenfolge bekommen zurück, die besagten, :

My favorite site is <a href="http://www.example.com">http://www.example.com</a>. What is yours? 

Nachdem die Bahn für eine Weile zum Scheuern, war ich endlich in der Lage zusammen Teile Stück von verschiedenen Ausdrücken, die mir helfen, das zu tun, wonach ich suche (Einige Beispiele enthalten abschließende Punkte am Ende von URLs in der tatsächlichen URL, einige codieren URLs bereits in Anker-Tags usw.)

Hier ist, was ich so habe weit:

public static String toLinkifiedString(String s, IAnchorBuilder anchorBuilder) 
{ 
    if (IsNullOrEmpty(s)) 
    { 
     return Empty; 
    } 

    String r = "(?<![=\"\"\\/>])(www\\.|(http|https|ftp|news|file)(s)?://)([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?([^.|'|# |!])"; 

    Pattern pattern = Pattern.compile(r, Pattern.DOTALL | Pattern.UNIX_LINES | Pattern.CASE_INSENSITIVE); 
    Matcher matcher = pattern.matcher(s); 
    if (anchorBuilder != null) 
    { 
     return matcher.replaceAll(anchorBuilder.createAnchorFromUrl("$0")); 
    } 
    return matcher.replaceAll("<a href=\"$0\">$0</a>"); // group 0 is the whole expression 
} 

public interface IAnchorBuilder 
{ 
    public String createAnchorFromUrl(String url); 
} 

es gibt auch einfache verion von toLinkifiedString, die nur die Zeichenfolge nimmt s - es ruft nur toLinkifiedString (s, null)

So wie ich schon sagte, wird dieses Muster alles kontrollieren Ich brauche es zu fangen und den Ersatz eAll funktioniert gut für jeden Fall, außer wenn ein Link mit www beginnt. Wenn die Übereinstimmung mit "www" statt einem Protokoll wie "http" oder "ftp" beginnt, möchte ich "http: //" vor dem resultierenden Link bedingt voraussetzen. Das heißt:

MyClass.toLinkifiedString("go to www.example.org") 

go to <a href="http://www.example.com">www.example.org</a> 

Die passenden Gruppen zurückkehren soll, sind wie folgt:

  • $ 0 - die tatsächliche URL, die gefunden wird: http://www.example.org oder www.example. net
  • $ 1 - das Protokoll Spiel ("http: // "Oder "www" für Links w/o Protokolle)

Ich nehme an, was ich in Pseudo-Code zu tun, in der Lage sein will, ist so etwas wie:

matcher.replaceAll("<a href="(if protocol = "www", insert "http://" + url - otherwise, insert url">url</a>" 

Ist das möglich? Oder soll ich einfach nur glücklich sein mit der Lage, nur Anker von Links zu erstellen, die mit „http: // ...“ :)

Vielen Dank für jede Hilfe jedermann

+0

Sie müssen _quite_ nicht so viele Backslashes verwenden. : D –

+0

@ mjd79: Deine Regex ist ziemlich durcheinander. Selbst wenn Sie anfangen, den Dreh raus zu bekommen, sollten Sie keine Beispiele aus dem Internet kopieren, ohne zu verstehen, was sie bedeuten. Ich kann viele falsche Annahmen darin sehen (über das richtige Entweichen von Charakter und über die Mechanik von Charakterklassen). Die Frage, wie man eine URL in einem Text findet, war schon oft da, ich schlage vor, dass du SO mit Google vergleichst. Zumindest die Regexe kommen hier meist mit einer bewiesenen Erklärung. :) – Tomalak

Antwort

9

Für Ihr spezielles Problem, auf jeden Fall mit einer Callback-Funktion gehen, wie Tomalak sagt.

Für das Problem all dieser Schrägstriche, und die verschiedenen anderen Merkwürdigkeiten ...

Hier Ihre aktuelle Regex Spaltung zwischen Zeilen Java ist:

(?<![=\"\"\\/>]) 
(www\\.|(http|https|ftp|news|file)(s)?://) 
([\\w+?\\.\\w+])+ 
([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)? 
([^.|'|# |!]) 

Und das gleiche wie ein Nicht-Java regex (keine Java-String entkommt):

(?<![=""\/>]) 
(www\.|(http|https|ftp|news|file)(s)?://) 
([\w+?\.\w+])+ 
([a-zA-Z0-9\~\!\@\#\$\%\^\&amp;\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)? 
([^.|'|# |!]) 


Und hier ist eine Beschreibung dessen, was mit ihm los ist ... :)

Linie ein - Sie " in der Zeichenklasse sind Duplizieren und brauchen nicht /

Linie zwei zu entkommen - ok, außer ich bin nicht sicher, was Sie nach mit der (s)? Teil, da Sie https in der vorherigen Gruppe sowieso haben.

Zeile drei - Sie wissen, dass Sie dort eine Zeichenklasse haben? Quantifizierer funktionieren nicht. Sie möchten wahrscheinlich (\w+?\.\w+)+ stattdessen. (Das ist (\\w+?\\.\\w+)+ in einer Java-Zeichenfolge.)

Linie vier - wow, was für eine Menge zu entkommen !! Fast alles unnötig. Geben Sie diese ein zu gehen: ([[email protected]#$%^&*()_\-=+\/?.:;',]*)? (und wieder: ([[email protected]#$%^&*()_\\-=+\\/?.:;',]*)?)

Linie fünf - Wechsel macht nichts innerhalb einer Zeichenklasse. Dies wird tun: [^.'#!], und fügen Sie eine einzige |, wenn Sie tatsächlich verhindern wollen, dass die Rohrkohle dort sein.

alle diese Kommentare zusammen Putting stellt diese regex:

(?<![="/>]) 
(www\.|(http|https|ftp|news|file)://) 
(\w+?\.\w+)+ 
([[email protected]#$%^&*()_\-=+\/?.:;',]*)? 
([^.'# !]) 

Oder noch einmal, mit Flucht für Java:

(?<![=\"/>]) 
(www\\.|(http|https|ftp|news|file)://) 
(\\w+?\\.\\w+)+ 
([[email protected]#$%^&*()_\\-=+\\/?.:;',]*)? 
([^.'# !]) 

Beachten Sie, wie viel einfacher das ist!

für die wieder auf eine einzige Zeile gehen gibt:

(?<![="/>])(www\.|(http|https|ftp|news|file)://)(\w+?\.\w+)+([[email protected]#$%^&*()_\-=+\/?.:;',]*)?([^.'# !]) 

oder

(?<![=\"/>])(www\\.|(http|https|ftp|news|file)://)(\\w+?\\.\\w+)+([[email protected]#$%^&*()_\\-=+\\/?.:;',]*)?([^.'# !]) 

Aber ich zu dem mehrzeiligen eines kleben würde - plonk nur (?x) am Anfang und es ist ein eine gültige Regex, die den Whitespace ignoriert, und Sie können #s zum Kommentieren verwenden - immer eine gute Sache mit Regexen, solange dies der Fall ist!

+0

+1 für die Zeit nehmen! :-) – Tomalak

+0

Obwohl wahrscheinlich hätte ich die Flucht der Backslashes und Zitate weggelassen, da dies eine Java String-Anforderung ist, keine Regex-Anforderung. Ein Großteil der Ungewissheit rührt von der Tatsache her, dass die Menschen immer wieder verwirren, was für ein Entkommen von welchem ​​System verlangt wird - die Erfahrenen, weil sie wissen, dass die Unerfahrenen ironisch sind, weil sie es nicht tun. – Tomalak

+0

Hmmm, guter Punkt. Ich bin gegangen und habe Beispiele hinzugefügt, ohne der Antwort zu entkommen. Hoffentlich habe ich es nicht zu verwirrend gemacht, beides zu haben ... vielleicht sollte ich die Java-Karten komplett entfernen und nur ein paar Zeilen über die Flucht haben? –

4

bieten Sieht aus wie Sie in Not sind einer Callback-Funktion, die ein dynamisches Ergebnis zurückgibt, das Sie anstelle der festen Zeichenfolge verwenden können, die Sie derzeit in replaceAll() haben.

Ich denke, man etwas aus der akzeptierten Antwort auf diese Frage stellen kann: Java equivalent to PHP's preg_replace_callback.

+2

Hier ist noch eins: http: // elliotth.blogspot.com/2004/07/java-implementation-of-rubys-gsub.html –