2009-06-10 52 views
2

我是一個相對newb當談到正則表達式,但我開始得到它的竅門。我開始在java中編寫一個方法來「鏈接」一個字符串 - 也就是說,掃描它的任何URL地址(即「http:// ...」)或看起來類似的網址(「www。 example.com ...「)有條件的替換正則表達式

因此,舉例來說,如果我有一個看起來像這樣的字符串:

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

通過該方法運行它之後,你會得到一個字符串返回該說:

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

經過一段時間的網絡沖刷後,我終於能夠拼湊零件不同的表達方式,幫助我做我在找什麼(一些例子包括網址末尾的實際url,一些編碼網址已經在錨標籤等)

這是我所擁有的遠:

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

還有toLinkifiedString只需要字符串小號簡單的優化版本 - 它只是調用toLinkifiedString(S,NULL)

所以就像我說的,這種模式捕捉一切我需要它來抓住和替換除了鏈接以www開頭時,eAll對於每種情況都非常有用。如果匹配以「www」而不是協議開頭,比如「http」或「ftp」,我想在結果鏈接的前面有條件地加上「http://」。那就是:

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

應該返回

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

匹配分組如下:

  • $ 0 - 是被發現的實際網址:http://www.example.orgwww.example。 net
  • $ 1 - 協議匹配(「http:// 「或者‘WWW’的鏈接W/O協議)

我想什麼,我希望能夠做到,在僞代碼是一樣的東西:

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

這可能嗎?或者我應該感到高興能夠只創建鏈接,首先錨的「http:// ...」 :)

感謝所有幫助任何人都可以提供

+0

你不需要使用_quite_這麼多的反斜槓。 :D – 2009-06-10 15:20:27

+0

@ mjd79:你的正則表達式相當糟糕。即使你已經開始掌握它,你也不應該在沒有完全理解它們的意思的情況下將例子從互聯網上覆制下來。我可以看到很多錯誤的假設(關於正確的字符轉義和字符類的機制)。如何在文本中找到網址的問題在這裏已經有很多次了,我建議你通過Google的方式來查看。至少這裏的正則表達式通常帶有經過驗證的解釋。 :) – Tomalak 2009-06-10 18:00:43

回答

9

對於你的具體問題,絕對要像Tomalak說的那樣使用回調函數。

對於那些斜線的問題,以及各種其他怪事......

這裏是跨行您當前的Java正則表達式分裂:

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

而作爲一個非Java一樣的東西正則表達式(沒有Java字符串轉義):

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


這裏是什麼地方錯了一個說明... :)

行一個 - 你在字符類複製",並且不需要逃避/

線 - 好吧,除非我不知道你在做什麼用後因爲無論如何你在前面的組中都有https。

第三行 - 你知道你有一個角色班嗎?量詞不起作用。您可能需要(\w+?\.\w+)+。 (這是一個Java字符串中的(\\w+?\\.\\w+)+)。

第四行 - 哇,很多轉義!幾乎所有不必要的。這給一展身手:([[email protected]#$%^&*()_\-=+\/?.:;',]*)?(並再次:([[email protected]#$%^&*()_\\-=+\\/?.:;',]*)?

五線 - 輪替並不做一個字符類內部的任何物件。這會執行:[^.'#!],並添加一個單獨的|,如果您確實想防止管道字符在那裏。

把所有這些意見一併提供此正則表達式:

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

或者,再加上漏出的Java:

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

注意如何更簡單的是!

對單個線再回到給出:

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

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

但我會堅持到多一個 - 只要plonk的(?x)在起步時,這是一個有效的正則表達式會忽略空格,並且您可以使用#s進行註釋 - 只要這樣,對於正則表達式總是一件好事!

4

看起來您需要返回一個動態結果的回調函數,您可以使用它代替當前在replaceAll()中的固定字符串。

我想你可以從這個問題的接受答案中做出一些事情:Java equivalent to PHP's preg_replace_callback

+2

這是另一個:http:// elliotth。blogspot.com/2004/07/java-implementation-of-rubys-gsub.html – 2009-06-10 15:00:42