it-swarm.com.de

Regulärer Ausdruck für die äußeren Klammern

Ich brauche einen regulären Ausdruck, um den gesamten Text zwischen zwei äußeren Klammern auszuwählen.

Beispiel: some text(text here(possible text)text(possible text(more text)))end text

Ergebnis: (text here(possible text)text(possible text(more text)))

222
DaveF

Reguläre Ausdrücke sind das falsche Werkzeug für den Job, weil es sich um verschachtelte Strukturen handelt, d. H. Rekursion.

Dafür gibt es einen einfachen Algorithmus, den ich in dieser Antwort zu einer vorherigen Frage beschrieben habe. 

119
Frank

Ich möchte diese Antwort als Kurzreferenz hinzufügen. Fühlen Sie sich frei zu aktualisieren.


. NET Regex mit Bilanzkreise .

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

Wobei c als Tiefenzähler verwendet wird.

Demo bei Regexstorm.com


[~ # ~] pcre [~ # ~] unter Verwendung eines rekursiven Musters .

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

Demo bei regex101 ; Oder ohne Abwechslung:

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

Demo bei regex101 ; Oder abgerollt für die Leistung:

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

Demo bei regex101 ; Das Muster wird bei (?R) Eingefügt, was (?0) Darstellt.

Perl, PHP, Notepad ++ , [~ # ~] r [~ # ~]: Perl = TRUE , Python: Regex-Paket mit (?V1) Für Perl-Verhalten.


Ruby mit nterausdrucksaufrufe .

Mit Ruby 2.0 \g<0> Kann Full Pattern aufgerufen werden.

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

Demo bei Rubular ; Ruby 1.9 unterstützt nur Gruppenrekursion erfassen :

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

Demo bei Rubular ( atomare Gruppierung seit Ruby 1.9.3)


JavaScript API :: XRegExp.matchRecursive

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

JS, Java und andere reguläre Ausdrücke ohne Rekursion bis zu 2 Verschachtelungsebenen:

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

Demo bei regex101 . Tiefer Verschachtelung muss hinzugefügt werden zum Mustern.
Um bei unausgeglichenen Klammern schneller zu versagen lassen Sie den Quantifizierer + Fallen.


Java : Eine interessante Idee unter Verwendung von Vorwärtsreferenzen von @jaytea .


Referenz - Was bedeutet dieser reguläre Ausdruck?

94
bobble bubble

Sie können regex rekursion verwenden:

\(([^()]|(?R))*\)
82
rogal111
[^\(]*(\(.*\))[^\)]*

[^\(]* stimmt mit allem überein, was keine öffnende Klammer am Anfang der Zeichenfolge ist. (\(.*\)) erfasst die erforderliche, in Klammern eingeschlossene Teilzeichenfolge, und [^\)]* entspricht alles, was keine schließende Klammer am Ende der Zeichenfolge ist. Beachten Sie, dass dieser Ausdruck nicht versucht, Klammern zu finden. Ein einfacher Parser (siehe Dehmanns Antwort ) wäre dafür besser geeignet. 

26
Zach Scrivena
(?<=\().*(?=\))

Wenn Sie Text zwischen zwei passenden Klammern auswählen möchten, haben Sie mit regulären Ausdrücken kein Glück. Das ist unmöglich(*).

Diese Regex gibt nur den Text zwischen der ersten Öffnung und den letzten schließenden Klammern in Ihrer Zeichenfolge zurück.


(*) Es sei denn, Ihre Regex-Engine verfügt über Funktionen wie Balancing Groups oder Rekursion . Die Anzahl der Engines, die solche Funktionen unterstützen, wächst langsam, aber sie sind immer noch nicht allgemein verfügbar. 

14
Tomalak

Es ist tatsächlich möglich, .NET-reguläre Ausdrücke zu verwenden, aber es ist nicht trivial, lesen Sie es daher sorgfältig. 

Sie können einen schönen Artikel hier lesen. Möglicherweise müssen Sie sich auch mit regulären Ausdrücken von .NET vertraut machen. Sie können hier lesen.

Die spitzen Klammern <> wurden verwendet, da für sie kein Escaping erforderlich ist.

Der reguläre Ausdruck sieht folgendermaßen aus:

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

Diese Antwort erklärt die theoretische Einschränkung, warum reguläre Ausdrücke nicht das richtige Werkzeug für diese Aufgabe sind.


Reguläre Ausdrücke können dies nicht tun. 

Reguläre Ausdrücke basieren auf einem Rechenmodell, das als Finite State Automata (FSA) bezeichnet wird. Wie der Name schon sagt, kann sich ein FSA nur an den aktuellen Zustand erinnern, er hat keine Informationen zu den vorherigen Zuständen.

 FSA

In dem obigen Diagramm sind S1 und S2 zwei Zustände, bei denen S1 der Start- und Endschritt ist. Wenn wir also mit dem String 0110 versuchen, geht der Übergang wie folgt vor:

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

In den obigen Schritten, wenn wir uns an zweiter Stelle S2 befinden, d. H. Nach dem Analysieren von 01 von 0110, hat die FSA keine Informationen über den vorherigen 0 in 01, da sie sich nur an den aktuellen Zustand und das nächste Eingabesymbol erinnert.

Bei dem obigen Problem müssen wir das Nein zum Öffnen von Klammern kennen. Das heißt, es muss an einer Stelle gespeichert sein. Da FSAs das nicht kann, kann ein regulärer Ausdruck nicht geschrieben werden.

Es kann jedoch ein Algorithmus geschrieben werden, um das Ziel zu erreichen. Algorithmen fallen im Allgemeinen unter Pushdown Automata (PDA). PDA ist eine Ebene über FSA. PDA hat einen zusätzlichen Stack, um etwas zu speichern. PDAs können verwendet werden, um das obige Problem zu lösen, da wir die öffnenden Klammern im Stapel "Push" und "pop" sie öffnen können, sobald wir eine schließende Klammer sehen. Wenn am Ende der Stapel leer ist, werden Klammern geöffnet und Klammern geschlossen. Sonst nicht. 

Eine ausführliche Diskussion finden Sie hier

4
Somnath Musib

Dies ist die definitive Regex:

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

)*
)
\)

Beispiel:

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

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

beachten Sie, dass der '(pip' korrekt als Zeichenfolge . verwaltet wird (versucht im Regler: http://sourceforge.net/projects/regulator/ ). 

3
Marco

Der reguläre Ausdruck mit Ruby (Version 1.9.3 oder höher):

/(?<match>\((?:\g<match>|[^()]++)*\))/

Demo auf Rubular

2
Joy Hu

Sie benötigen die erste und letzte Klammer. Verwenden Sie so etwas wie:

str.indexOf ('('); - gibt Ihnen das erste Vorkommen

str.lastIndexOf (')'); - Letzter

Du brauchst also einen String zwischen 

String searchedString = str.substring(str1.indexOf('('),str1.lastIndexOf(')');
2
Shell Scott

Ich habe eine kleine JavaScript-Bibliothek mit dem Namen balanced geschrieben, um diese Aufgabe zu unterstützen. Sie können dies erreichen, indem Sie tun

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

Sie können sogar ersetzen:

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

Hier ist ein komplexeres und interaktiveres Beispiel JSFiddle .

2
Chad Scira

Hier ist eine anpassbare Lösung, die Einzelzeichenliteral-Trennzeichen in Java ermöglicht:

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

Verwendungsbeispiel:

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

Dieser hat auch funktioniert

re.findall(r'\(.+\)', s)
0
DataScienceStep

Die Antwort hängt davon ab, ob Sie übereinstimmende Klammergruppen oder nur das erste Öffnen bis zum letzten Schließen im Eingabetext anpassen müssen.

Wenn Sie übereinstimmende geschachtelte Klammern verwenden müssen, benötigen Sie mehr als reguläre Ausdrücke. - siehe @dehmann

Wenn es erstmal offen ist, um zuletzt zu schließen, siehe @Zach

Entscheiden Sie, was mit Ihnen geschehen soll:

abc ( 123 ( foobar ) def ) xyz ) ghij

Sie müssen entscheiden, welcher Code in diesem Fall übereinstimmen muss.

0
Douglas Leeder

Dies kann für einige nützlich sein:

Analysiert Parmeter aus Funktionszeichenfolgen (mit verschachtelten Strukturen) in Javascript

Übereinstimmende Strukturen wie:
 Parse parmeters from function string 

  • entspricht Klammern, eckigen Klammern, Klammern, einfachen und doppelten Anführungszeichen

Hier können Sie generierte reguläre Ausdrücke in Aktion sehen

/**
 * get param content of function string.
 * only params string should be provided without parentheses
 * WORK even if some/all params are not set
 * @return [param1, param2, param3]
 */
exports.getParamsSAFE = (str, nbParams = 3) => {
    const nextParamReg = /^\s*((?:(?:['"([{](?:[^'"()[\]{}]*?|['"([{](?:[^'"()[\]{}]*?|['"([{][^'"()[\]{}]*?['")}\]])*?['")}\]])*?['")}\]])|[^,])*?)\s*(?:,|$)/;
    const params = [];
    while (str.length) { // this is to avoid a BIG performance issue in javascript regexp engine
        str = str.replace(nextParamReg, (full, p1) => {
            params.Push(p1);
            return '';
        });
    }
    return params;
};

Hiermit wird die OP-Frage nicht vollständig beantwortet, aber ich bin der Meinung, dass es für einige, die hierher kommen, nützlich sein kann, nach verschachtelten regexp-Strukturen zu suchen.

0
538ROMEO
"""
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 __== "__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"
0
Gene Olson