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)))
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)))
Respuestas:
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 .
Quiero agregar esta respuesta para referencia rápida. Siéntase libre de actualizar.
.NET Regex utilizando grupos de equilibrio .
\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\)
Donde c
se usa como el contador de profundidad.
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 .
(?>[^)(]+|(?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).
(...(..)..(..)..(..)..(..)..)
) 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.
Puede usar la recursividad regex :
\(([^()]|(?R))*\)
Unrecognized grouping construct
.
[^\(]*(\(.*\))[^\)]*
[^\(]*
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.
(?<=\().*(?=\))
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.
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 FSA
puede recordar solo el estado actual, no tiene información sobre los estados anteriores.
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 S2
es decir, después del análisis 01
de 0110
la FSA no tiene información acerca de la anterior 0
en 01
que 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 FSAs
no 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)
. PDA
es 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.
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)(?!))
>
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/ )
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 .
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 .
re
Solució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)))]
La expresión regular que usa Ruby (versión 1.9.3 o superior):
/(?<match>\((?:\g<match>|[^()]++)*\))/
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(')');
"""
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"
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.
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' } ]
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í
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.
Este también funcionó
re.findall(r'\(.+\)', s)
Esto puede ser útil para algunos:
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.