Expresión regular para que coincida con paréntesis equilibrados

290

Necesito una expresión regular para seleccionar todo el texto entre dos corchetes externos.

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

Resultado: (text here(possible text)text(possible text(more text)))

DaveF
fuente
3
Esta pregunta es muy pobre porque no está claro lo que está preguntando. Todas las respuestas lo interpretaron de manera diferente. @DaveF, ¿puedes aclarar la pregunta?
Matt Fenwick
1
Respondido en esta publicación: stackoverflow.com/questions/6331065/…
sship21

Respuestas:

144

Las expresiones regulares son la herramienta incorrecta para el trabajo porque se trata de estructuras anidadas, es decir, recursividad.

Pero hay un algoritmo simple para hacer esto, que describí en esta respuesta a una pregunta anterior .

Franco
fuente
15
La implementación de .NET tiene [Balancing Group Definitions msdn.microsoft.com/en-us/library/… que permiten este tipo de cosas.
Carl G
22
No estoy de acuerdo con que las expresiones regulares sean la herramienta incorrecta para esto por varias razones. 1) La mayoría de las implementaciones de expresiones regulares tienen una solución viable, si no perfecta, para esto. 2) A menudo está tratando de encontrar pares equilibrados de delimitadores en un contexto en el que también están en juego otros criterios adecuados para las expresiones regulares. 3) A menudo está entregando una expresión regular en alguna API que solo acepta expresiones regulares y no tiene otra opción.
Kenneth Baltrinic
20
Regex es la herramienta CORRECTA para el trabajo. Esta respuesta no es correcta. Ver la respuesta de rogal111.
Andrew
44
Absolutamente de acuerdo con la respuesta. Aunque hay algunas implementaciones de recursividad en regexp, son iguales a las máquinas de estado finito y no se supone que funcionen con estructuras anidadas, pero las Gramáticas sin contexto hacen esto. Mire la jerarquía de las gramáticas formales de Homsky.
Nick Roz el
138

Quiero agregar esta respuesta para referencia rápida. Siéntase libre de actualizar.


.NET Regex utilizando grupos de equilibrio .

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

Donde cse usa como el contador de profundidad.

Demo en Regexstorm.com


PCRE utilizando un patrón recursivo .

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

Demo en regex101 ; O sin alternancia:

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

Demo en regex101 ; O desenrollado para el rendimiento:

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

Demo en regex101 ; El patrón se pega en el (?R)que representa (?0).

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


Ruby usando llamadas subexpresoras .

Con Ruby 2.0 \g<0>se puede usar para llamar al patrón completo.

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

Demo en Rubular ; Ruby 1.9 solo admite la captura de recursividad grupal :

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

Demostración en Rubular  ( agrupación atómica desde Ruby 1.9.3)


 API de JavaScript :: XRegExp.matchRecursive

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

JS, Java y otros sabores de expresiones regulares sin recurrencia hasta 2 niveles de anidamiento:

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

Demo en regex101 . Se necesita agregar una anidación más profunda al patrón.
Para fallar más rápido en paréntesis desequilibrados, suelte el +cuantificador.


Java : una idea interesante utilizando referencias avanzadas de @jaytea .


Referencia: ¿qué significa esta expresión regular?

burbuja bobble
fuente
1
Cuando repites un grupo con un cuantificador posesivo, es inútil hacer que ese grupo sea atómico ya que todas las posiciones de retroceso en ese grupo se eliminan en cada repetición. Entonces escribir (?>[^)(]+|(?R))*+es lo mismo que escribir (?:[^)(]+|(?R))*+. Lo mismo para el próximo patrón. Acerca de la versión desenrollada, puede colocar un cuantificador posesivo aquí: [^)(]*+para evitar el retroceso (en caso de que no haya un corchete de cierre).
Casimir et Hippolyte
Sobre el patrón Ruby 1.9, en lugar de hacer que el grupo repetido sea atómico (que tiene un interés limitado cuando hay muchos paréntesis anidados (...(..)..(..)..(..)..(..)..)) en la cadena de asunto), puede usar un grupo simple que no captura y encerrar todo en un grupo atómico: (?>(?:[^)(]+|\g<1>)*)( esto se comporta exactamente como un cuantificador posesivo). En Ruby 2.x, el cuantificador posesivo está disponible.
Casimir et Hippolyte
@CasimiretHippolyte ¡Gracias! Ajusté los patrones PCRE y para Ruby 1.9, ¿te refieres a que todo el patrón sea así ? Por favor, siéntase libre de actualizarse. Entiendo lo que quieres decir, pero no estoy seguro de si hay muchas mejoras.
Bubble Bubble
117

Puede usar la recursividad regex :

\(([^()]|(?R))*\)
rogal111
fuente
3
Un ejemplo sería realmente útil aquí, no puedo hacer que esto funcione para cosas como "(1, (2, 3)) (4, 5)".
Andy Hayden
44
@AndyHayden esto se debe a que "(1, (2, 3)) (4, 5)" tiene dos grupos separados con espacio. Use mi expresión regular con bandera global: / (([^ ()] | (? R)) *) / g. Aquí está la prueba en línea: regex101.com/r/lF0fI1/1
rogal111
1
Hice una pregunta sobre esto la semana pasada stackoverflow.com/questions/26385984/recursive-pattern-in-regex
Andy Hayden
77
En .NET 4.5 me sale el siguiente error para este patrón: Unrecognized grouping construct.
nam
3
¡Increíble! Esta es una gran característica de regex. Gracias por ser el único que realmente responde la pregunta. Además, ese sitio regex101 es dulce.
Andrew
28
[^\(]*(\(.*\))[^\)]*

[^\(]*coincide con todo lo que no es un corchete de apertura al comienzo de la cadena, (\(.*\))captura la subcadena requerida entre corchetes y [^\)]*coincide con todo lo que no es un corchete de cierre al final de la cadena. Tenga en cuenta que esta expresión no intenta hacer coincidir los corchetes; un analizador simple (ver la respuesta de dehmann ) sería más adecuado para eso.

Zach Scrivena
fuente
el corchete dentro de la clase no necesita ser escapado. Ya que por dentro no es un metacharacted.
José Leal
10
Este expr falla contra algo como "texto (texto) texto (texto) texto" retornando "(texto) texto (texto)". Las expresiones regulares no pueden contar corchetes.
Christian Klauser
17
(?<=\().*(?=\))

Si desea seleccionar texto entre dos paréntesis coincidentes , no tiene suerte con las expresiones regulares. Esto es imposible (*) .

Esta expresión regular solo devuelve el texto entre el primer paréntesis de apertura y el último de cierre en su cadena.


(*) A menos que su motor de expresiones regulares tenga características como grupos de equilibrio o recursividad . La cantidad de motores que admiten tales características está creciendo lentamente, pero todavía no están disponibles comúnmente.

Tomalak
fuente
¿Qué significan los signos "<=" y "="? ¿A qué motor de expresiones regulares se dirige esta expresión?
Christian Klauser
1
Esto es mirar alrededor, o más correctamente "aserciones de ancho cero para mirar hacia adelante / mirar hacia atrás". La mayoría de los motores regex modernos los admiten.
Tomalak
Según el ejemplo del OP, él quiere incluir a los padres más externos en el partido. Esta expresión regular los tira a la basura.
Alan Moore
1
@ Alan M: Tienes razón. Pero de acuerdo con el texto de la pregunta, él quiere todo entre los padres más externos. Elige tu elección. Dijo que había estado intentando durante horas, por lo que ni siquiera consideró "todo, incluidos los padres más externos" como la intención, porque es muy trivial: "(. *)".
Tomalak
3
@ghayes La respuesta es de 2009. Eso fue hace mucho tiempo; Los motores de expresión regular que permiten alguna forma de recursión han sido más infrecuentes de lo que son ahora (y aún son bastante infrecuentes). Lo mencionaré en mi respuesta.
Tomalak
14

Esta respuesta explica la limitación teórica de por qué las expresiones regulares no son la herramienta adecuada para esta tarea.


Las expresiones regulares no pueden hacer esto.

Las expresiones regulares se basan en un modelo informático conocido como Finite State Automata (FSA). Como su nombre lo indica, a FSApuede recordar solo el estado actual, no tiene información sobre los estados anteriores.

FSA

En el diagrama anterior, S1 y S2 son dos estados donde S1 es el paso inicial y final. Entonces, si intentamos con la cadena 0110, la transición es la siguiente:

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

En los pasos anteriores, cuando estamos en el segundo S2es decir, después del análisis 01de 0110la FSA no tiene información acerca de la anterior 0en 01que sólo puede recordar el estado actual y el siguiente símbolo de entrada.

En el problema anterior, necesitamos saber el no de paréntesis de apertura; Esto significa que debe almacenarse en algún lugar. Pero como FSAsno puede hacer eso, no se puede escribir una expresión regular.

Sin embargo, se puede escribir un algoritmo para realizar esta tarea. Los algoritmos son generalmente cae bajo Pushdown Automata (PDA). PDAes un nivel por encima de FSA. PDA tiene una pila adicional para almacenar información adicional. Los PDA se pueden usar para resolver el problema anterior, porque podemos " push" el paréntesis de apertura en la pila y " pop" una vez que encontramos un paréntesis de cierre. Si al final, la pila está vacía, entonces se abren los paréntesis de apertura y cierre. De otra forma no.

musibs
fuente
1
Aquí hay varias respuestas, lo que demuestra que ES posible.
Jiří Herník
1
@Marco Esta respuesta habla sobre expresiones regulares en perspectiva teórica. ¡Muchos motores regex hoy en día no solo confían en este modelo teórico y usan memoria adicional para hacer el trabajo!
musibs
@ JiříHerník: esas no son expresiones regulares en sentido estricto: no están definidas como expresiones regulares por Kleene . Algunos motores de expresión regular han implementado algunas capacidades adicionales, haciéndolos analizar más que solo lenguajes regulares .
Willem Van Onsem
12

En realidad, es posible hacerlo utilizando expresiones regulares .NET, pero no es trivial, así que léalo detenidamente.

Puedes leer un buen artículo aquí . También es posible que necesite leer en expresiones regulares .NET. Puedes empezar a leer aquí .

<>Se usaron corchetes angulares porque no requieren escapar.

La expresión regular se ve así:

<
[^<>]*
(
    (
        (?<Open><)
        [^<>]*
    )+
    (
        (?<Close-Open>>)
        [^<>]*
    )+
)*
(?(Open)(?!))
>
Alexander Bartosh
fuente
4

Esta es la expresión regular definitiva:

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

)*
)
\)

Ejemplo:

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

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

tenga en cuenta que '(pip'se gestiona correctamente como una cadena. (probado en el regulador: http://sourceforge.net/projects/regulator/ )

Marco
fuente
4

He escrito una pequeña biblioteca de JavaScript llamada balanceada para ayudar con esta tarea. Puedes lograr esto haciendo

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

Incluso puedes hacer reemplazos:

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

Aquí hay un ejemplo más complejo e interactivo de JSFiddle .

Chad Scira
fuente
4

Además de la respuesta de bobble bubble , hay otros sabores regex donde se admiten construcciones recursivas.

Lua

Uso %b()( %b{}/ %b[]para llaves / corchetes):

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

Perl6 :

Coincide con paréntesis equilibrados múltiples no superpuestos:

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

Superposición de coincidencias de paréntesis equilibrados múltiples:

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

Ver demo .

reSolución Python no regex

Vea la respuesta de poke para Cómo obtener una expresión entre paréntesis equilibrados .

Solución Java no regex personalizable

Aquí hay una solución personalizable que permite delimitadores literales de un solo carácter en 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;
    }
}

Uso de la muestra:

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)))]
Wiktor Stribiżew
fuente
Vea una demostración en línea de Java para una prueba de que funciona con múltiples coincidencias.
Wiktor Stribiżew
3

La expresión regular que usa Ruby (versión 1.9.3 o superior):

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

Demo en rubular

Alegría hu
fuente
3

Necesitas el primer y último paréntesis. Usa algo como esto:

str.indexOf ('('); - te dará la primera aparición

str.lastIndexOf (')'); - el último

Entonces necesitas una cuerda entre,

String searchedString = str.substring(str1.indexOf('('),str1.lastIndexOf(')');
Shell Scott
fuente
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"
Gene Olson
fuente
0

La respuesta depende de si necesita hacer coincidir los conjuntos de paréntesis coincidentes, o simplemente la primera apertura hasta el último cierre en el texto de entrada.

Si necesita hacer coincidir los corchetes anidados, entonces necesita algo más que expresiones regulares. - ver @dehmann

Si solo se abre por primera vez y se cierra, vea @Zach

Decide con qué quieres que suceda:

abc ( 123 ( foobar ) def ) xyz ) ghij

Debe decidir qué debe coincidir con su código en este caso.

Douglas Leeder
fuente
3
Esta no es una respuesta.
Alan Moore el
Sí, la demanda de un cambio en la pregunta debe darse como un comentario,
Gangnus
0

porque js regex no admite la coincidencia recursiva, no puedo hacer que la combinación de paréntesis equilibrados funcione.

así que este es un javascript simple para la versión de bucle que convierte la cadena "método (arg)" en una matriz

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)

el resultado es como

[ { 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' } ]
basura
fuente
0

Si bien muchas respuestas mencionan esto de alguna forma al decir que regex no admite la coincidencia recursiva, etc., la razón principal de esto radica en las raíces de la Teoría de la Computación.

Idioma de la forma {a^nb^n | n>=0} is not regular. Regex solo puede coincidir con elementos que forman parte del conjunto regular de idiomas.

Leer más @ aquí

Prakhar Agrawal
fuente
0

No utilicé regex ya que es difícil lidiar con código anidado. Por lo tanto, este fragmento debería permitirle tomar secciones de código con corchetes equilibrados:

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

Utilicé esto para extraer fragmentos de código de un archivo de texto.

Daniel
fuente
0

También me quedé atrapado en esta situación donde vienen los patrones anidados.

La expresión regular es lo correcto para resolver el problema anterior. Usar debajo del patrón

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

Este también funcionó

re.findall(r'\(.+\)', s)
DataScienceStep
fuente
-1

Esto puede ser útil para algunos:

Analiza los parámetros de la cadena de función (con estructuras anidadas) en javascript

Combina estructuras como:
Analizar los parámetros de la cadena de función

  • coincide con corchetes, corchetes, paréntesis, comillas simples y dobles

Aquí puedes ver expresiones regulares generadas en acción

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

Esto no aborda completamente la pregunta de OP, pero creo que puede ser útil para algunos que vienen aquí a buscar expresiones regulares de estructura anidadas.

538ROMEO
fuente