Espressione regolare per abbinare parentesi bilanciate


290

Ho bisogno di un'espressione regolare per selezionare tutto il testo tra due parentesi esterne.

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

Risultato: (text here(possible text)text(possible text(more text)))


3
Questa domanda è molto scarsa perché non è chiaro cosa si stia chiedendo. Tutte le risposte hanno interpretato diversamente. @DaveF puoi chiarire la domanda per favore?
Matt Fenwick,

1
Ha risposto in questo post: stackoverflow.com/questions/6331065/...
sship21

Risposte:


144

Le espressioni regolari sono lo strumento sbagliato per il lavoro perché hai a che fare con strutture nidificate, ad esempio ricorsione.

Ma c'è un semplice algoritmo per farlo, che ho descritto in questa risposta a una domanda precedente .


15
L'implementazione di .NET ha [Balancing Group Definitions msdn.microsoft.com/en-us/library/… che consentono questo genere di cose.
Carl G,

22
Non sono d'accordo sul fatto che le espressioni regolari siano lo strumento sbagliato per questo per alcuni motivi. 1) La maggior parte delle implementazioni di espressioni regolari ha una soluzione praticabile se non perfetta per questo. 2) Spesso stai cercando di trovare coppie equilibrate di delimitatori in un contesto in cui sono in gioco anche altri criteri adatti alle espressioni regolari. 3) Spesso stai trasferendo un'espressione regolare in qualche API che accetta solo espressioni regolari e non hai scelta.
Kenneth Baltrinic,


20
Regex è lo strumento GIUSTO per il lavoro. Questa risposta non è giusta Vedi la risposta di rogal111.
Andrew, il

4
Assolutamente d'accordo con la risposta. Sebbene ci siano alcune implementazioni della ricorsione in regexp, sono uguali a macchine a stati finiti e non si suppone che lavorino con strutture nidificate, ma le grammatiche libere da contesto lo fanno. Guarda la gerarchia di grammatiche formali di Homsky.
Nick Roz,

138

Voglio aggiungere questa risposta per il riferimento rapido. Sentiti libero di aggiornare.


.NET Regex utilizzando gruppi di bilanciamento .

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

Dove cviene utilizzato come contatore di profondità.

Demo su Regexstorm.com


PCRE usando un modello ricorsivo .

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

Demo su regex101 ; O senza alternanza:

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

Demo su regex101 ; O srotolato per le prestazioni:

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

Demo su regex101 ; Il modello viene incollato in corrispondenza del (?R)quale rappresenta (?0).

Perl, PHP, Notepad ++, R : perl = TRUE , Python : pacchetto Regex con (?V1)comportamento Perl.


Ruby usando chiamate in sottoespressione .

Con Ruby 2.0 \g<0>può essere utilizzato per chiamare il modello completo.

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

Demo su Rubular ; Ruby 1.9 supporta solo l' acquisizione della ricorsione del gruppo :

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

Demo su Rubular  ( raggruppamento atomico da Ruby 1.9.3)


 API JavaScript :: XRegExp.matchRecursive

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

JS, Java e altri regex flavours senza ricorsione fino a 2 livelli di annidamento:

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

Demo su regex101 . L' annidamento più profondo deve essere aggiunto al modello.
Per fallire più velocemente su parentesi sbilanciate, rilasciare+ quantificatore.


Java : un'idea interessante che utilizza riferimenti diretti di @jaytea .


Riferimento - Cosa significa questo regex?


1
Quando si ripete un gruppo con un quantificatore possessivo, è inutile rendere quel gruppo atomico poiché tutte le posizioni di backtracking in quel gruppo vengono eliminate ad ogni ripetizione. Quindi scrivere (?>[^)(]+|(?R))*+è uguale a scrivere (?:[^)(]+|(?R))*+. Stessa cosa per il prossimo modello. Per quanto riguarda la versione non srotolata, puoi inserire un quantificatore possessivo qui: [^)(]*+per evitare il backtracking (nel caso in cui non ci siano parentesi quadre).
Casimir et Hippolyte,

A proposito del modello Ruby 1.9, invece di rendere atomico il gruppo ripetuto (che ha un interesse limitato quando ci sono molte parentesi nidificate (...(..)..(..)..(..)..(..)..)) nella stringa del soggetto), puoi usare un semplice gruppo non catturante e racchiudere tutto in un gruppo atomico: (?>(?:[^)(]+|\g<1>)*)( questo si comporta esattamente come un quantificatore possessivo). In Ruby 2.x è disponibile il quantificatore possessivo.
Casimir et Hippolyte,

@CasimiretHippolyte Grazie! Ho adattato i pattern PCRE e per Ruby 1.9, vuoi dire che l'intero pattern è così ? Sentiti libero di aggiornarti. Capisco cosa intendi, ma non sono sicuro che ci siano molti miglioramenti.
bolla bobble

117

È possibile utilizzare la ricorsione regex :

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

3
Un esempio sarebbe davvero utile qui, non riesco a farlo funzionare per cose come "(1, (2, 3)) (4, 5)".
Andy Hayden,

4
@AndyHayden questo perché "(1, (2, 3)) (4, 5)" ha due gruppi separati da spazio. Usa il mio regexp con flag globale: / (([^ ()] | (? R)) *) / g. Ecco il test online: regex101.com/r/lF0fI1/1
rogal111

1
Ho fatto una domanda su questa settimana scorsa stackoverflow.com/questions/26385984/recursive-pattern-in-regex
Andy Hayden,

7
In .NET 4.5 ottengo il seguente errore per questo modello: Unrecognized grouping construct.
nam

3
Eccezionale! Questa è una grande caratteristica di regex. Grazie per essere l'unico a rispondere effettivamente alla domanda. Inoltre, quel sito regex101 è dolce.
Andrew, il

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

[^\(]*corrisponde a tutto ciò che non è una parentesi quadra aperta all'inizio della stringa, (\(.*\))acquisisce la sottostringa richiesta racchiusa tra parentesi quadre e [^\)]*corrisponde a tutto ciò che non è una parentesi quadra chiusa alla fine della stringa. Nota che questa espressione non tenta di abbinare le parentesi; un semplice parser (vedi la risposta di Dehmann ) sarebbe più adatto a questo.


la parentesi all'interno della classe non deve essere sfuggita. Dal momento che all'interno non è un metacaratto.
José Leal,

10
Questo expr fallisce contro qualcosa come "testo (testo) testo (testo) testo" ritorno "(testo) testo (testo)". Le espressioni regolari non possono contare le parentesi.
Christian Klauser,

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

Se si desidera selezionare il testo tra due parentesi corrispondenti , si è sfortunati con le espressioni regolari. Questo è impossibile (*) .

Questa regex restituisce semplicemente il testo tra la prima e l'ultima parentesi chiusa nella stringa.


(*) A meno che il tuo motore regex non abbia funzionalità come gruppi di bilanciamento o ricorsione . Il numero di motori che supportano tali funzionalità sta lentamente crescendo, ma non sono ancora comunemente disponibili.


Cosa significano i segni "<=" e "="? A quale motore regexp è rivolta questa espressione?
Christian Klauser,

1
Si tratta di asserzioni look-around, o più correttamente "zero looker look-ahead / look-behind". La maggior parte dei moderni motori regex li supporta.
Tomalak,

Secondo l'esempio del PO, vuole includere le parentesi più esterne nella partita. Questa regex li butta via.
Alan Moore,

1
@Alan M: hai ragione. Ma secondo il testo della domanda, vuole tutto tra le parentesi più esterne. Scegli la tua scelta Ha detto che ci provava da ore, quindi non ha nemmeno considerato "tutto compreso le parentesi più esterne" come l'intenzione, perché è così banale: "(. *)".
Tomalak,

3
@ghayes La risposta è del 2009. È molto tempo fa; i motori di espressione regolare che consentono una qualche forma di ricorsione sono stati più rari di quanto non lo siano ora (e sono ancora piuttosto rari). Lo citerò nella mia risposta.
Tomalak,

14

Questa risposta spiega la limitazione teorica del perché le espressioni regolari non sono lo strumento giusto per questo compito.


Le espressioni regolari non possono farlo.

Le espressioni regolari si basano su un modello di elaborazione noto come Finite State Automata (FSA). Come indica il nome, a FSApuò ricordare solo lo stato corrente, non ha informazioni sugli stati precedenti.

FSA

Nel diagramma sopra, S1 e S2 sono due stati in cui S1 è il passaggio iniziale e finale. Quindi, se proviamo con la stringa 0110, la transizione procede come segue:

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

Nei passaggi precedenti, quando siamo al secondo S2ossia dopo l'analisi 01del 0110la FSA non ha informazioni sul precedente 0in 01quanto può solo ricordare lo stato corrente e il simbolo di ingresso successiva.

Nel problema sopra, dobbiamo conoscere il no dell'apertura della parentesi; questo significa che deve essere conservato in qualche luogo. Ma poiché FSAsnon è possibile farlo, non è possibile scrivere un'espressione regolare.

Tuttavia, è possibile scrivere un algoritmo per eseguire questa attività. Gli algoritmi sono generalmente sotto Pushdown Automata (PDA). PDAè un livello sopra di FSA. PDA ha uno stack aggiuntivo per memorizzare alcune informazioni aggiuntive. I PDA possono essere utilizzati per risolvere il problema di cui sopra, perché possiamo ' push' la parentesi di apertura nella pila e ' pop' quando incontriamo una parentesi di chiusura. Se alla fine, la pila è vuota, quindi aprendo le parentesi e chiudendo le corrispondenze tra parentesi. Altrimenti no.



1
Ci sono diverse risposte qui, che provano, è possibile.
Jiří Herník,

1
@Marco Questa risposta parla di espressioni regolari in prospettiva teorica. Molti motori regex al giorno non si basano solo su questo modello teorico e usano un po 'di memoria aggiuntiva per fare il lavoro!
musibs,

@JiříHerník: quelle non sono espressioni regolari in senso stretto: non definite come espressioni regolari da Kleene . Alcuni motori di espressione regolare hanno effettivamente implementato alcune funzionalità extra, rendendoli più che semplici lingue .
Willem Van Onsem,

12

In realtà è possibile farlo utilizzando le espressioni regolari .NET, ma non è banale, quindi leggi attentamente.

Puoi leggere un bell'articolo qui . Potrebbe anche essere necessario leggere le espressioni regolari .NET. Puoi iniziare a leggere qui .

Le parentesi angolari <>sono state utilizzate perché non richiedono la fuga.

L'espressione regolare si presenta così:

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

4

Questa è la regex definitiva:

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

)*
)
\)

Esempio:

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

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

si noti che il '(pip'è gestito correttamente come stringa. (provato nel regolatore: http://sourceforge.net/projects/regulator/ )


4

Ho scritto una piccola libreria JavaScript denominata bilanciata per aiutare con questo compito. Puoi farlo facendo

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

Puoi persino fare sostituzioni:

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

Ecco un esempio più complesso e interattivo di JSFiddle .


4

Aggiungendo alla risposta di bobble bubble , ci sono altri sapori regex in cui sono supportati costrutti ricorsivi.

Lua

Usa %b()( %b{}/ %b[]per parentesi graffe / parentesi quadre):

  • for s in string.gmatch("Extract (a(b)c) and ((d)f(g))", "%b()") do print(s) end(vedi demo )

Perl6 :

Partite tra parentesi multiple bilanciate non sovrapposte:

my regex paren_any { '(' ~ ')' [ <-[()]>+ || <&paren_any> ]* }
say "Extract (a(b)c) and ((d)f(g))" ~~ m:g/<&paren_any>/;
# => (「(a(b)c)」 「((d)f(g))」)

Partite tra parentesi multiple bilanciate sovrapposte:

say "Extract (a(b)c) and ((d)f(g))" ~~ m:ov:g/<&paren_any>/;
# => (「(a(b)c)」 「(b)」 「((d)f(g))」 「(d)」 「(g)」)

Vedi la demo .

reSoluzione Python non regex

Vedi la risposta di Poke per Come ottenere un'espressione tra parentesi bilanciate .

Soluzione non regex personalizzabile Java

Ecco una soluzione personalizzabile che consente delimitatori letterali a carattere singolo in 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;
    }
}

Esempio di utilizzo:

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)))]

Guarda una demo Java online per una prova che funziona con più corrispondenze.
Wiktor Stribiżew,

3

L'espressione regolare usando Ruby (versione 1.9.3 o successiva):

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

Demo su rubular


3

Sono necessarie la prima e l'ultima parentesi. Usa qualcosa del genere:

str.indexOf ('('); - ti darà la prima occorrenza

str.lastIndexOf ( ')'); - l'ultimo

Quindi hai bisogno di una stringa tra,

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

1
"""
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"

0

La risposta dipende dal fatto che sia necessario abbinare set di parentesi corrispondenti o semplicemente dalla prima apertura all'ultima chiusura nel testo di input.

Se devi abbinare parentesi nidificate corrispondenti, hai bisogno di qualcosa di più delle espressioni regolari. - vedi @dehmann

Se è aperto solo per l'ultima chiusura, vedere @Zach

Decidi cosa vuoi che accada:

abc ( 123 ( foobar ) def ) xyz ) ghij

In questo caso devi decidere a quale codice deve corrispondere il codice.


3
Questa non è una risposta
Alan Moore,

Sì, la richiesta di un cambiamento nella domanda dovrebbe essere data come commento,
Gangnus,

0

poiché js regex non supporta la corrispondenza ricorsiva, non riesco a far funzionare la corrispondenza tra parentesi bilanciate.

quindi questa è una semplice versione javascript per loop che trasforma la stringa "method (arg)" in array

push(number) map(test(a(a()))) bass(wow, abc)
$$(groups) filter({ type: 'ORGANIZATION', isDisabled: { $ne: true } }) pickBy(_id, type) map(test()) as(groups)
const parser = str => {
  let ops = []
  let method, arg
  let isMethod = true
  let open = []

  for (const char of str) {
    // skip whitespace
    if (char === ' ') continue

    // append method or arg string
    if (char !== '(' && char !== ')') {
      if (isMethod) {
        (method ? (method += char) : (method = char))
      } else {
        (arg ? (arg += char) : (arg = char))
      }
    }

    if (char === '(') {
      // nested parenthesis should be a part of arg
      if (!isMethod) arg += char
      isMethod = false
      open.push(char)
    } else if (char === ')') {
      open.pop()
      // check end of arg
      if (open.length < 1) {
        isMethod = true
        ops.push({ method, arg })
        method = arg = undefined
      } else {
        arg += char
      }
    }
  }

  return ops
}

// const test = parser(`$$(groups) filter({ type: 'ORGANIZATION', isDisabled: { $ne: true } }) pickBy(_id, type) map(test()) as(groups)`)
const test = parser(`push(number) map(test(a(a()))) bass(wow, abc)`)

console.log(test)

il risultato è come

[ { method: 'push', arg: 'number' },
  { method: 'map', arg: 'test(a(a()))' },
  { method: 'bass', arg: 'wow,abc' } ]
[ { method: '$$', arg: 'groups' },
  { method: 'filter',
    arg: '{type:\'ORGANIZATION\',isDisabled:{$ne:true}}' },
  { method: 'pickBy', arg: '_id,type' },
  { method: 'map', arg: 'test()' },
  { method: 'as', arg: 'groups' } ]

0

Mentre così tante risposte menzionano questo in qualche modo dicendo che regex non supporta la corrispondenza ricorsiva e così via, la ragione principale di ciò risiede nelle radici della Teoria del calcolo.

Lingua del modulo {a^nb^n | n>=0} is not regular. Regex può abbinare solo cose che fanno parte del normale set di lingue.

Leggi di più @ qui


0

Non ho usato regex poiché è difficile gestire il codice nidificato. Quindi questo frammento dovrebbe essere in grado di permetterti di prendere sezioni di codice con parentesi bilanciate:

def extract_code(data):
    """ returns an array of code snippets from a string (data)"""
    start_pos = None
    end_pos = None
    count_open = 0
    count_close = 0
    code_snippets = []
    for i,v in enumerate(data):
        if v =='{':
            count_open+=1
            if not start_pos:
                start_pos= i
        if v=='}':
            count_close +=1
            if count_open == count_close and not end_pos:
                end_pos = i+1
        if start_pos and end_pos:
            code_snippets.append((start_pos,end_pos))
            start_pos = None
            end_pos = None

    return code_snippets

L'ho usato per estrarre frammenti di codice da un file di testo.


0

Sono stato anche bloccato in questa situazione in cui arrivano schemi nidificati.

L'espressione regolare è la cosa giusta per risolvere il problema sopra riportato. Usa il modello seguente

'/(\((?>[^()]+|(?1))*\))/'


-1

Questo può essere utile per alcuni:

Analizza i parametri dalla stringa di funzioni (con strutture nidificate) in JavaScript

Abbina strutture come:
Parametri di analisi dalla stringa di funzioni

  • corrisponde a parentesi quadre, parentesi quadre, parentesi, virgolette singole e doppie

Qui puoi vedere regexp generato in azione

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

Questo non affronta in modo completo la domanda del PO, ma penso che potrebbe essere utile per alcuni venire qui per cercare regexp di strutture nidificate.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.