2012-07-03 93 views
5

是否有API方法返回與正則表達式匹配的所有子字符串(可能重疊)?與java正則表達式匹配的所有重疊子串

例如,我有一個文本字符串:String t = 04/31 412-555-1235;,我有一個模式:Pattern p = new Pattern("\\d\\d+");匹配兩個或多個字符的字符串。

我得到的匹配爲:04,31,412,555,1235

如何獲得重疊的比賽嗎?

我想要的代碼返回:04,31,41,412,12,55,555,55,12,123,1235,23,235,35

理論上應該是可能 - 有一個明顯的O(n^2)算法枚舉和檢查所有的模式與子字符串。

EDIT

不是枚舉所有子串,它是安全使用region(int start, int end)方法Matcher。根據單獨提取的子字符串檢查模式可能會改變匹配的結果(例如,如果在模式的開始/結尾處存在非捕獲組或字邊界檢查)。

EDIT 2

事實上,目前還不清楚是否region()做你所期望的零寬度匹配。該規範含糊不清,實驗產生令人失望的結果。

例如:

String line = "xx90xx"; 
String pat = "\\b90\\b"; 
System.out.println(Pattern.compile(pat).matcher(line).find()); // prints false 
for (int i = 0; i < line.length(); ++i) { 
    for (int j = i + 1; j <= line.length(); ++j) { 
    Matcher m = Pattern.compile(pat).matcher(line).region(i, j); 
    if (m.find() && m.group().size == (j - i)) { 
     System.out.println(m.group() + " (" + i + ", " + j + ")"); // prints 90 (2, 4) 
    } 
    } 
} 

我不知道最好的解決方法是什麼。一種方法是取line的子串並在檢查pat是否匹配之前填充適當的邊界字符。

編輯3

這裏是我想出了一個完整的解決方案。它可以處理原始正則表達式中的零寬度模式,邊界等。它會查看文本字符串的所有子字符串,並檢查正則表達式是否僅在特定位置匹配,方法是在開頭和結尾填充具有適當數量通配符的模式。它似乎適用於我嘗試的病例 - 雖然我沒有做過廣泛的測試。它肯定比它的效率低。

public static void allMatches(String text, String regex) 
    { 
    for (int i = 0; i < text.length(); ++i) { 
     for (int j = i + 1; j <= text.length(); ++j) { 
     String positionSpecificPattern = "((?<=^.{"+i+"})("+regex+")(?=.{"+(text.length() - j)+"}$))"; 
     Matcher m = Pattern.compile(positionSpecificPattern).matcher(text); 

     if (m.find()) 
     { 
      System.out.println("Match found: \"" + (m.group()) + "\" at position [" + i + ", " + j + ")"); 
     } 
     } 
    } 
    } 

EDIT 4

這裏是這樣做的更好的方式:https://stackoverflow.com/a/11372670/244526

EDIT 5

JRegex庫支持查找所有重疊的子串匹配的Java正則表達式(雖然似乎沒有在一段時間內更新)。具體而言,documentation on non-breaking search指定:

使用非間斷搜索可以找到一個 圖案的所有可能occureneces,包括那些相交或嵌套。這是 通過使用匹配器的方法proceed()而不是find()來實現

+0

只是通過所有3個或更多字符結果做一個post-regex循環 –

+0

http://regexlib.com/可能是一個很好的地方做一些挖掘。 –

+0

@Ωmega盡我所能,但對反饋沒有用處。乾杯。 –

回答

0

最接近你可以得到的是這樣的。

"(?=((\\d*)\\d))(?=(\\d)\\d*)" 

的結果將是捕獲組1,2和3

至於我的想象可以走了,我只能認爲在零長度斷言捕獲作爲一種可行的方式來奪回的一個字符串的相同位置。在零長度斷言之外捕獲文本會一勞永逸地消耗文本(後視圖只能捕獲Java中的固定長度,因此可以認爲它是無法訪問的)。

該解決方案並不完美:除了重複(文本在同一位置!)和空字符串匹配,它不會捕獲所有可能的子字符串。捕獲所有可能的子

一種方法是構造如下的正則表達式與從1開始的n值:

"(?=(\\d{" + n + "}))" 

和匹配對這個字符串遞增的n值,直到沒有匹配。

與使用「\ d +」匹配所有數字並提取所有子字符串的方法相比,此方法當然效率低下。

0

它是可行的作爲爲O(n)僅當指定允許號碼長度的範圍內。

比方說從2-4的數字(數字00-9999):(?=(\\d{2}))(?=(\\1\\d)?)(?=(\\2\\d)?)

這是通過正先行零長度斷言,捕捉這種先行成組。結果是可以在正則表達式輸入中找到的所有2-4位字符串的數組,以及重複項和空字符串(用於非匹配捕獲)。

我不是Java開發人員,但我相信Perl腳本也可以作爲示例閱讀。

#!/usr/bin/perl          # perl script 
use List::MoreUtils qw/ uniq /;      # uniq subroutine library 
$_ = '04/31 412-555-1235';       # input 
my @n = uniq (/(?=(\d{2}))(?=(\1\d)?)(?=(\2\d)?)/g); # regex (single slash in Perl) 
print "$_\n" for grep(/\S/, @n);      # print non-empty lines 

訣竅是使用反向引用。如果您想要捕獲2-5位數的字符串,則需要在正則表達式中使用更多的正向預覽:(?=(\\d{2}))(?=(\\1\\d)?)(?=(\\2\\d)?)(?=(\\3\\d)?)

我相信這是您可以做出的最接近的方法。如果這適用於您,請發表評論,並希望某些Java開發人員將編輯我的答案與上面的腳本的Java代碼。

+0

正則表達式是在Java中是一樣的(除了反斜線需要被轉義)。至於'uniq',它可以用Java中的'Set'('TreeSet'或'HashSet')來模擬。 – nhahtdh

+0

@nhahtdh - 謝謝。請隨時通過編輯帖子將更新添加到我的答案中。 –

1

我面臨類似的情況,我嘗試了上面的答案,但在我的情況下,它通過設置匹配器 的開始和結束索引花費了太多時間,但我認爲我找到了更好的解決方案,我張貼在這裏爲他人。 所以下面是我的代碼sniplet。

if (textToParse != null) { 
Matcher matcher = PLACEHOLDER_PATTERN.matcher(textToParse); 
    while(matcher.hitEnd()!=true){ 
     Boolean result = matcher.find(); 
     int count = matcher.groupCount(); 
     System.out.println("Result " +result+" count "+count); 
     if(result==true && count==1){ 
      mergeFieldName = matcher.group(1); 
      mergeFieldNames.add(mergeFieldName); 
      } 
     } 
    } 

我已經使用matcher.hitEnd()方法來檢查我是否已到達文本的末尾。

希望這會有所幫助。 謝謝!

相關問題