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 FSA
pode 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 S2
ou seja, após a análise 01
de 0110
, a FSA não tem nenhuma informação sobre o anterior 0
em 01
como 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 FSAs
nã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 .
re
Soluçã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.