Eu preciso de uma expressão regular para selecionar todo o texto entre dois colchetes externos.
Exemplo: some text(text here(possible text)text(possible text(more text)))end text
Resultado: (text here(possible text)text(possible text(more text)))
Eu preciso de uma expressão regular para selecionar todo o texto entre dois colchetes externos.
Exemplo: some text(text here(possible text)text(possible text(more text)))end text
Resultado: (text here(possible text)text(possible text(more text)))
Respostas:
Expressões regulares são a ferramenta errada para o trabalho porque você está lidando com estruturas aninhadas, ou seja, recursão.
Mas existe um algoritmo simples para fazer isso, que descrevi nesta resposta a uma pergunta anterior .
Quero adicionar esta resposta para uma referência rápida. Sinta-se livre para atualizar.
Regex do .NET usando grupos de balanceamento .
\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\)
Onde cé usado como contador de profundidade.
PCRE usando um padrão recursivo .
\((?:[^)(]+|(?R))*+\)
Demonstração em regex101 ; Ou sem alternância:
\((?:[^)(]*(?R)?)*+\)
Demonstração em regex101 ; Ou desenrolado para desempenho:
\([^)(]*+(?:(?R)[^)(]*)*+\)
Demonstração em regex101 ; O padrão é colado no (?R)qual representa (?0).
Perl, PHP, Notepad ++, R : perl = TRUE , pacote Python : Regex com (?V1)comportamento Perl.
Ruby usando chamadas de subexpressão .
Com Ruby 2.0 \g<0>pode ser usado para chamar padrão completo.
\((?>[^)(]+|\g<0>)*\)
Demonstração no Rubular ; O Ruby 1.9 suporta apenas a captura de recursão de grupo :
(\((?>[^)(]+|\g<1>)*\))
Demonstração no Rubular ( agrupamento atômico desde Ruby 1.9.3)
API JavaScript :: XRegExp.matchRecursive
XRegExp.matchRecursive(str, '\\(', '\\)', 'g');
JS, Java e outros tipos de expressões regulares sem recursão até 2 níveis de aninhamento:
\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)
Demonstração em regex101 . Aninhamento mais profundo precisa ser adicionado ao padrão.
Para falhar mais rápido em parênteses desequilibrados, solte o +quantificador.
Java : Uma ideia interessante usando referências futuras de @jaytea .
(?>[^)(]+|(?R))*+é o mesmo que escrever (?:[^)(]+|(?R))*+. A mesma coisa para o próximo padrão. Sobre a versão desenrolada, você pode colocar um quantificador possessivo aqui: [^)(]*+para impedir o retorno (no caso de não haver colchete).
(...(..)..(..)..(..)..(..)..)) na sequência do assunto), você pode usar um grupo não capturador simples e incluir tudo em um grupo atômico: (?>(?:[^)(]+|\g<1>)*)( isso se comporta exatamente como um quantificador possessivo). No Ruby 2.x, o quantificador possessivo está disponível.
Você pode usar a recursão de regex :
\(([^()]|(?R))*\)
Unrecognized grouping construct.
[^\(]*(\(.*\))[^\)]*
[^\(]*corresponde a tudo que não é um colchete de abertura no início da sequência, (\(.*\))captura a substring necessária entre colchetes e [^\)]*corresponde a tudo que não é um colchete de fechamento no final da sequência. Observe que esta expressão não tenta combinar colchetes; um simples analisador (veja a resposta de dehmann ) seria mais adequado para isso.
(?<=\().*(?=\))
Se você quiser selecionar texto entre dois correspondentes parênteses, você está fora de sorte com expressões regulares. Isso é impossível (*) .
Essa regex apenas retorna o texto entre a primeira abertura e os últimos parênteses de fechamento em sua string.
(*) A menos que seu mecanismo de expressão regular tenha recursos como grupos de balanceamento ou recursão . O número de mecanismos que suportam esses recursos está crescendo lentamente, mas eles ainda não estão disponíveis.
Esta resposta explica a limitação teórica de por que expressões regulares não são a ferramenta certa para esta tarefa.
Expressões regulares não podem fazer isso.
Expressões regulares são baseadas em um modelo de computação conhecido como Finite State Automata (FSA). Como o nome indica, um FSApode lembrar apenas o estado atual, não possui informações sobre os estados anteriores.
No diagrama acima, S1 e S2 são dois estados em que S1 é a etapa inicial e final. Portanto, se tentarmos com a string 0110, a transição será a seguinte:
0 1 1 0
-> S1 -> S2 -> S2 -> S2 ->S1
Nos passos acima, quando estamos em segundo S2ou seja, após a análise 01de 0110, a FSA não tem nenhuma informação sobre o anterior 0em 01como ele só consegue se lembrar o estado atual eo próximo símbolo de entrada.
No problema acima, precisamos saber o não dos parênteses de abertura; isso significa que deve ser armazenado em algum lugar. Mas como FSAsnão é possível fazer isso, uma expressão regular não pode ser escrita.
No entanto, um algoritmo pode ser escrito para executar esta tarefa. Algoritmos são geralmente cai Pushdown Automata (PDA). PDAé um nível acima de FSA. O PDA possui uma pilha adicional para armazenar algumas informações adicionais. Os PDAs podem ser usados para resolver o problema acima, porque podemos ' push' o parêntese de abertura na pilha e ' pop' eles quando encontrarmos um parêntese de fechamento. Se, no final, a pilha estiver vazia, abra os parênteses e feche os parênteses. Caso contrário, não.
Na verdade, é possível fazê-lo usando expressões regulares do .NET, mas não é trivial, portanto, leia com atenção.
Você pode ler um bom artigo aqui . Você também pode precisar ler expressões regulares do .NET. Você pode começar a ler aqui .
Parênteses angulares <>foram usados porque não requerem escape.
A expressão regular é assim:
<
[^<>]*
(
(
(?<Open><)
[^<>]*
)+
(
(?<Close-Open>>)
[^<>]*
)+
)*
(?(Open)(?!))
>
Este é o regex definitivo:
\(
(?<arguments>
(
([^\(\)']*) |
(\([^\(\)']*\)) |
'(.*?)'
)*
)
\)
Exemplo:
input: ( arg1, arg2, arg3, (arg4), '(pip' )
output: arg1, arg2, arg3, (arg4), '(pip'
observe que o '(pip'é gerenciado corretamente como string. (experimentado no regulador: http://sourceforge.net/projects/regulator/ )
Eu escrevi uma pequena biblioteca JavaScript chamada equilibrada para ajudar nessa tarefa. Você pode conseguir isso fazendo
balanced.matches({
source: source,
open: '(',
close: ')'
});
Você pode até fazer substituições:
balanced.replacements({
source: source,
open: '(',
close: ')',
replace: function (source, head, tail) {
return head + source + tail;
}
});
Aqui está um exemplo mais complexo e interativo do JSFiddle .
Além da resposta do bobble bubble , existem outros tipos de expressões regulares em que as construções recursivas são suportadas.
Lua
Use %b()( %b{}/ %b[]para chaves / colchetes):
for s in string.gmatch("Extract (a(b)c) and ((d)f(g))", "%b()") do print(s) end(veja demonstração )Perl6 :
Correspondências de múltiplos parênteses equilibrados sem sobreposição:
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))」)
Sobreposição de vários parênteses balanceados:
say "Extract (a(b)c) and ((d)f(g))" ~~ m:ov:g/<&paren_any>/;
# => (「(a(b)c)」 「(b)」 「((d)f(g))」 「(d)」 「(g)」)
Veja a demonstração .
reSolução não regex Python
Veja a resposta do puxão em Como obter uma expressão entre parênteses balanceados .
Solução não regex personalizável em Java
Aqui está uma solução personalizável que permite delimitadores literais de um caractere em 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 da amostra:
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)))]
A expressão regular usando Ruby (versão 1.9.3 ou superior):
/(?<match>\((?:\g<match>|[^()]++)*\))/
Você precisa do primeiro e do último parênteses. Use algo como isto:
str.indexOf ('('); - ele fornecerá a primeira ocorrência
str.lastIndexOf (')'); - último
Então você precisa de uma string 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"
A resposta depende se você precisa corresponder a conjuntos de colchetes correspondentes ou apenas o primeiro aberto até o último fechamento no texto de entrada.
Se você precisar combinar colchetes aninhados correspondentes, precisará de algo mais do que expressões regulares. - Vejo @dehmann
Se for apenas o primeiro aberto para o último fechamento, veja @Zach
Decida com o que você deseja que aconteça:
abc ( 123 ( foobar ) def ) xyz ) ghij
Você precisa decidir com o que seu código deve corresponder neste caso.
Como o js regex não suporta correspondência recursiva, não consigo fazer o trabalho de correspondência entre parênteses balanceados.
portanto, este é um javascript simples para a versão de loop que transforma a string "method (arg)" na 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)
o resultado é 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' } ]
Enquanto tantas respostas mencionam isso de alguma forma, dizendo que o regex não suporta correspondência recursiva e assim por diante, a principal razão para isso está nas raízes da Teoria da Computação.
Idioma do formulário {a^nb^n | n>=0} is not regular . O Regex pode corresponder apenas a itens que fazem parte do conjunto regular de idiomas.
Leia mais @ aqui
Não usei regex, pois é difícil lidar com código aninhado. Portanto, esse snippet deve permitir que você pegue seções do código com colchetes balanceados:
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
Eu usei isso para extrair trechos de código de um arquivo de texto.
Este também funcionou
re.findall(r'\(.+\)', s)
Isso pode ser útil para alguns:
Aqui você pode ver o regexp gerado em ação
/**
* 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;
};
Isso não aborda completamente a questão do OP, mas eu acho que pode ser útil para alguns que vêm aqui procurar a estrutura aninhada regexp.