2009-02-13 276 views
197

我需要一個正則表達式來選擇兩個外括號之間的所有文本。正則表達式匹配外括號

例子:some text(text here(possible text)text(possible text(more text)))end text

結果:(text here(possible text)text(possible text(more text)))

我一直在嘗試了幾個小時,你要知道我的正則表達式的知識是不是我想它是:-)所以任何幫助會感激地收到。

+3

這個問題很差,因爲它不清楚它在問什麼。所有的答案都以不同的方式解釋。 @DaveF你能否澄清這個問題? – 2012-12-17 18:25:17

+1

回答這個帖子: http://stackoverflow.com/questions/6331065/matching-balanced-parenthesis-in-ruby-using-recursive-regular-expressions-like-p – sship21 2013-12-06 22:47:59

回答

106

正則表達式是工作的錯誤工具,因爲您正在處理嵌套結構,即遞歸。

但有一個簡單的算法來做到這一點,我把in this answer描述爲previous question

25
[^\(]*(\(.*\))[^\)]* 

[^\(]*匹配一切不是在字符串的開始處的開口支架,(\(.*\))捕獲括在括號內的所需的子串,並[^\)]*在字符串的末尾匹配的一切,是不是一個結束括號。請注意,此表達式不會嘗試匹配括號;一個簡單的解析器(見dehmann's answer)會更適合這個。

+0

類裏面的支架做不需要逃脫。由於裏面它不是一個metacharacted。 – 2009-02-13 15:59:05

+8

這個expr失敗了,例如「文本(文本)文本(文本)文本」返回「(文本)文本(文本)」。正則表達式不能計算括號。 – 2009-02-13 16:02:13

14
(?<=\().*(?=\)) 

如果你想兩個匹配括號之間選擇文本,你的運氣了正則表達式。這是不可能的(*)

這個正則表達式只是返回字符串中第一個開頭和最後一個關閉括號之間的文本。


(*)除非你的正則表達式引擎有一個像balancing groups or recursion功能。支持這些功能的引擎數量正在慢慢增長,但它們仍然不是常用的。

+0

「<=」和「=」符號是什麼意思?這種表達式定位的是什麼正則表達式引擎? – 2009-02-13 15:58:38

+1

這是環視或更正確的「零寬度預見/後顧斷言」。大多數現代正則表達式引擎都支持它們。 – Tomalak 2009-02-13 16:01:21

+0

根據OP的例子,他想在比賽中包括最外面的人。這個正則表達式會把它們扔掉。 – 2009-02-15 05:09:06

1

答案取決於您是否需要匹配括號的匹配組,或者僅僅是輸入文本中第一次打開到最後一次關閉。

如果您需要匹配匹配的嵌套括號,那麼您需要比正則表達式更多的東西。 - 看到@dehmann

如果只是先開到最後近看@Zach

決定要一起發生的事情:

abc (123 (foobar) def) xyz) ghij 

你需要決定你的代碼需要在這種情況下匹配的內容。

11

它實際上可以使用.NET正則表達式來完成,但它不是微不足道的,所以仔細閱讀。

您可以閱讀一篇好文章here。您也可能需要閱讀.NET正則表達式。你可以開始閱讀here。使用角括號<>是因爲它們不需要轉義。

正則表達式如下:

< 
[^<>]* 
(
    (
     (?<Open><) 
     [^<>]* 
    )+ 
    (
     (?<Close-Open>>) 
     [^<>]* 
    )+ 
)* 
(?(Open)(?!)) 
> 
3

這是明確的正則表達式:

\(
(?<arguments> 
( 
    ([^\(\)']*) | 
    (\([^\(\)']*\)) | 
    '(.*?)' 

)* 
) 
\) 

例子:

input: (arg1, arg2, arg3, (arg4), '(pip') 

output: arg1, arg2, arg3, (arg4), '(pip' 

注意,'(pip'正確的字符串管理。

2

我已經寫了一個名爲balanced的小型JavaScript庫來幫助完成此任務,您可以隨時進行plish該做

balanced.matches({ 
    source: source, 
    open: '(', 
    close: ')' 
}); 

,你甚至可以做替代

balanced.replacements({ 
    source: source, 
    open: '(', 
    close: ')', 
    replace: function (source, head, tail) { 
     return head + source + tail; 
    } 
}); 

繼承人更加複雜和交互式例如JSFiddle

52

我想添加這個答案quickreference。隨時更新。


.NET正則表達式使用balancing groups

\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\) 

其中c被用作深度計數器。

Demo at Regexstorm.com


PCRE

\((?>[^)(]+|(?R))*\) 

Demo at regex101;或者沒有改變:

\((?>[^)(]*(?R)?)*\) 

Demo at regex101。該圖案粘貼在代表(?0)(?R)

的Perl,PHP,記事本++,- [Rperl=TRUE的PythonRegex package(?V1)爲Perl行爲。


使用subexpression calls紅寶石

用Ruby 2.0 \g<0>可以用來調用全模式。

\((?>[^)(]+|\g<0>)*\) 

Demo at Rubular; Ruby 1.9的只支持capturing group recursion

(\((?>[^)(]+|\g<1>)*\)) 

Demo at Rubular  (atomic grouping因爲紅寶石1.9.3)


的JavaScript   API :: XRegExp.matchRecursive

XRegExp.matchRecursive(str, '\\(', '\\)', 'g'); 

JS,Java和其他正則表達式的口味,而不遞歸最多2級o f嵌套:

\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\) 

Demo at regex101。更深的nesting needs to be added模式。
要失敗快上不平衡括號drop the + quantifier.


的Java:一個有趣的idea using forward references by @jaytea


Reference - What does this regex mean?

1

這裏是一個定製的解決方案,允許單個字符文字分隔符在Java中:

public static List<String> getBalancedSubstrings(String s, Character markStart, 
           Character markEnd, Boolean includeMarkers) 

{ 
     List<String> subTreeList = new ArrayList<String>(); 
     int level = 0; 
     int lastOpenDelimiter = -1; 
     for (int i = 0; i < s.length(); i++) { 
      char c = s.charAt(i); 
      if (c == markStart) { 
       level++; 
       if (level == 1) { 
        lastOpenDelimiter = (includeMarkers ? i : i + 1); 
       } 
      } 
      else if (c == markEnd) { 
       if (level == 1) { 
        subTreeList.add(s.substring(lastOpenDelimiter, (includeMarkers ? i + 1 : i))); 
       } 
       if (level > 0) level--; 
      } 
     } 
     return subTreeList; 
    } 
} 

使用範例:

String s = "some text(text here(possible text)text(possible text(more text)))end text"; 
List<String> balanced = getBalancedSubstrings(s, '(', ')', true); 
System.out.println("Balanced substrings:\n" + balanced); 
// => [(text here(possible text)text(possible text(more text)))] 
1

所以你需要第一個和最後parenthess,使用不便這樣 str.indexOf( '('); - 它會給你第一次發生 str.lastIndexOf(')'); - 最後一個

所以你需要字符串, String searchingString = str.substring(str1.indexOf('('),str1。lastIndexOf( ')');

0
""" 
Here is a simple python program showing how to use regular 
expressions to write a paren-matching recursive parser. 

This parser recognises items enclosed by parens, brackets, 
braces and <> symbols, but is adaptable to any set of 
open/close patterns. This is where the re package greatly 
assists in parsing. 
""" 

import re 


# The pattern below recognises a sequence consisting of: 
# 1. Any characters not in the set of open/close strings. 
# 2. One of the open/close strings. 
# 3. The remainder of the string. 
# 
# There is no reason the opening pattern can't be the 
# same as the closing pattern, so quoted strings can 
# be included. However quotes are not ignored inside 
# quotes. More logic is needed for that.... 


pat = re.compile(""" 
    (.*?) 
    (\(| \) | \[ | \] | \{ | \} | \< | \> | 
          \' | \" | BEGIN | END | $) 
    (.*) 
    """, re.X) 

# The keys to the dictionary below are the opening strings, 
# and the values are the corresponding closing strings. 
# For example "(" is an opening string and ")" is its 
# closing string. 

matching = { "(" : ")", 
      "[" : "]", 
      "{" : "}", 
      "<" : ">", 
      '"' : '"', 
      "'" : "'", 
      "BEGIN" : "END" } 

# The procedure below matches string s and returns a 
# recursive list matching the nesting of the open/close 
# patterns in s. 

def matchnested(s, term=""): 
    lst = [] 
    while True: 
     m = pat.match(s) 

     if m.group(1) != "": 
      lst.append(m.group(1)) 

     if m.group(2) == term: 
      return lst, m.group(3) 

     if m.group(2) in matching: 
      item, s = matchnested(m.group(3), matching[m.group(2)]) 
      lst.append(m.group(2)) 
      lst.append(item) 
      lst.append(matching[m.group(2)]) 
     else: 
      raise ValueError("After <<%s %s>> expected %s not %s" % 
          (lst, s, term, m.group(2))) 

# Unit test. 

if __name__ == "__main__": 
    for s in ("simple string", 
       """ "double quote" """, 
       """ 'single quote' """, 
       "one'two'three'four'five'six'seven", 
       "one(two(three(four)five)six)seven", 
       "one(two(three)four)five(six(seven)eight)nine", 
       "one(two)three[four]five{six}seven<eight>nine", 
       "one(two[three{four<five>six}seven]eight)nine", 
       "oneBEGINtwo(threeBEGINfourENDfive)sixENDseven", 
       "ERROR testing (((mismatched))] parens"): 
     print "\ninput", s 
     try: 
      lst, s = matchnested(s) 
      print "output", lst 
     except ValueError as e: 
      print str(e) 
    print "done" 
1

正則表達式不能這樣做。

正則表達式基於被稱爲Finite State Automata (FSA)的計算模型。正如名稱所示,FSA只能記住當前狀態,它沒有關於以前狀態的信息。

FSA

在上述圖中,S1和S2是兩種狀態,其中S1是起始和最終步驟。因此,如果我們用字符串0110嘗試,轉型去如下:

 0  1  1  0 
-> S1 -> S2 -> S2 -> S2 ->S1 

在上面的步驟,當我們正處於第二S2即解析的011001之後,FSA沒有關於以前0信息在01,因爲它只能記住當前狀態和下一個輸入符號。

在上面的問題中,我們需要知道左括號的否;這意味着它必須在存儲在某個地方。但由於FSAs不能這樣做,所以不能寫正則表達式。

但是,可以編寫一個算法來實現目標。算法通常屬於Pushdown Automata (PDA)PDAFSA高一級。 PDA有一個額外的堆棧來存儲一些東西。 PDA可以用來解決上述問題,因爲我們可以'push'堆棧中的左括號和'pop'他們一旦我們遇到一個右括號。如果最後堆棧爲空,則打開括號和右括號匹配。否則不是。

的詳細討論可以發現here