Мне нужно регулярное выражение, чтобы выделить весь текст в двух внешних скобках.
Пример: some text(text here(possible text)text(possible text(more text)))end text
Результат: (text here(possible text)text(possible text(more text)))
Мне нужно регулярное выражение, чтобы выделить весь текст в двух внешних скобках.
Пример: some text(text here(possible text)text(possible text(more text)))end text
Результат: (text here(possible text)text(possible text(more text)))
Ответы:
Регулярные выражения являются неподходящим инструментом для работы, потому что вы имеете дело с вложенными структурами, то есть рекурсией.
Но для этого есть простой алгоритм, который я описал в ответе на предыдущий вопрос .
Я хочу добавить этот ответ для быстрой ссылки. Не стесняйтесь обновлять.
.NET Regex с использованием балансировочных групп .
\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\)
Где c
используется в качестве счетчика глубины.
PCRE с использованием рекурсивного шаблона .
\((?:[^)(]+|(?R))*+\)
Демо на regex101 ; Или без чередования:
\((?:[^)(]*(?R)?)*+\)
Демо на regex101 ; Или развернут для производительности:
\([^)(]*+(?:(?R)[^)(]*)*+\)
Демо на regex101 ; Узор наклеен на (?R)
который представляет (?0)
.
Perl, PHP, Notepad ++, R : perl = TRUE , Python : Regex пакет с (?V1)
для поведения Perl.
Использование Ruby вызовов подвыражений .
С Ruby 2.0 \g<0>
можно использовать для вызова полного шаблона.
\((?>[^)(]+|\g<0>)*\)
Демонстрация в Rubular ; Ruby 1.9 поддерживает только групповую рекурсию :
(\((?>[^)(]+|\g<1>)*\))
Демо в Rubular ( атомная группировка начиная с Ruby 1.9.3)
JavaScript API :: XRegExp.matchRecursive
XRegExp.matchRecursive(str, '\\(', '\\)', 'g');
JS, Java и другие регулярные выражения без рекурсии до 2 уровней вложенности:
\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)
Демонстрация на regex101 . Более глубокое вложение должно быть добавлено к образцу.
Чтобы быстрее потерпеть неудачу при несбалансированной скобке, отбросьте +
квантификатор.
Java : интересная идея с использованием прямых ссылок @jaytea .
(?>[^)(]+|(?R))*+
- это то же самое, что и письмо (?:[^)(]+|(?R))*+
. То же самое для следующего шаблона. Что касается развернутой версии, вы можете поместить здесь собственнический квантификатор: [^)(]*+
для предотвращения возврата (если нет закрывающей скобки).
(...(..)..(..)..(..)..(..)..)
) в строке темы), вы можете использовать простую группу без захвата и заключить все в атомарную группу: (?>(?:[^)(]+|\g<1>)*)
( это ведет себя точно так же как притяжательный квантификатор). В Ruby 2.x доступен квантификатор притяжений.
Вы можете использовать регулярные выражения :
\(([^()]|(?R))*\)
Unrecognized grouping construct
.
[^\(]*(\(.*\))[^\)]*
[^\(]*
сопоставляет все, что не является открывающей скобкой в начале строки, (\(.*\))
захватывает требуемую подстроку, заключенную в скобки, и [^\)]*
сопоставляет все, что не является закрывающей скобкой в конце строки. Обратите внимание, что это выражение не пытается сопоставить скобки; простой парсер (см . ответ Дехмана ) был бы более подходящим для этого.
(?<=\().*(?=\))
Если вы хотите выбрать текст в двух совпадающих скобках, вам не повезло с регулярными выражениями. Это невозможно (*) .
Это регулярное выражение просто возвращает текст между первым открывающим и последним закрывающими скобками в вашей строке.
(*) Если ваш движок регулярных выражений не имеет таких функций, как балансировка групп или рекурсия . Количество движков, поддерживающих такие функции, медленно растет, но они все еще не являются общедоступными.
Этот ответ объясняет теоретическое ограничение того, почему регулярные выражения не являются подходящим инструментом для этой задачи.
Регулярные выражения не могут этого сделать.
Регулярные выражения основаны на вычислительной модели, известной как Finite State Automata (FSA)
. Как видно из названия, a FSA
может запомнить только текущее состояние, в нем нет информации о предыдущих состояниях.
На приведенной выше диаграмме S1 и S2 представляют собой два состояния, где S1 является начальным и последним этапом. Так что, если мы попробуем со строкой 0110
, переход будет следующим:
0 1 1 0
-> S1 -> S2 -> S2 -> S2 ->S1
В приведенных выше шагов, когда мы находимся на втором , S2
т.е. после разбора 01
из 0110
, АФН не имеет никакой информации о предыдущей 0
в 01
как он может вспомнить только текущее состояние и следующий входной символ.
В вышеупомянутой проблеме нам нужно знать номер открывающей скобки; это означает, что он должен храниться в каком-то месте. Но так как FSAs
не может этого сделать, регулярное выражение не может быть написано.
Однако для этой задачи можно написать алгоритм. Алгоритмы, как правило, подпадают под Pushdown Automata (PDA)
. PDA
на один уровень выше FSA
. КПК имеет дополнительный стек для хранения дополнительной информации. КПК могут быть использованы для решения вышеуказанной проблемы, потому что мы можем « push
открывать круглые скобки в стеке» и pop
«их», когда встречаем закрывающие скобки. Если в конце стек пуст, то открывающая и закрывающая скобки совпадают. В противном случае нет.
На самом деле это возможно сделать с помощью регулярных выражений .NET, но это не тривиально, поэтому читайте внимательно.
Вы можете прочитать хорошую статью здесь . Вам также может понадобиться прочитать регулярные выражения .NET. Вы можете начать читать здесь .
Угловые скобки <>
были использованы, потому что они не требуют побега.
Регулярное выражение выглядит так:
<
[^<>]*
(
(
(?<Open><)
[^<>]*
)+
(
(?<Close-Open>>)
[^<>]*
)+
)*
(?(Open)(?!))
>
Это окончательное регулярное выражение:
\(
(?<arguments>
(
([^\(\)']*) |
(\([^\(\)']*\)) |
'(.*?)'
)*
)
\)
Пример:
input: ( arg1, arg2, arg3, (arg4), '(pip' )
output: arg1, arg2, arg3, (arg4), '(pip'
обратите внимание, что '(pip'
правильно управляется как строка. (пробовал в регуляторе: http://sourceforge.net/projects/regulator/ )
Я написал небольшую библиотеку JavaScript под названием сбалансированный, чтобы помочь с этой задачей. Вы можете сделать это, выполнив
balanced.matches({
source: source,
open: '(',
close: ')'
});
Вы даже можете сделать замены:
balanced.replacements({
source: source,
open: '(',
close: ')',
replace: function (source, head, tail) {
return head + source + tail;
}
});
Вот более сложный и интерактивный пример JSFiddle .
В добавление к ответу «всплывающего пузыря» есть и другие варианты регулярных выражений, в которых поддерживаются рекурсивные конструкции.
Lua
Используйте %b()
( %b{}
/ %b[]
для фигурных скобок / квадратных скобок):
for s in string.gmatch("Extract (a(b)c) and ((d)f(g))", "%b()") do print(s) end
(см. демонстрацию )Perl6 :
Неперекрывающиеся совпадения с несколькими сбалансированными скобками:
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))」)
Перекрывающиеся совпадения с несколькими сбалансированными скобками:
say "Extract (a(b)c) and ((d)f(g))" ~~ m:ov:g/<&paren_any>/;
# => (「(a(b)c)」 「(b)」 「((d)f(g))」 「(d)」 「(g)」)
Смотрите демо .
Python re
решение без регулярных выражений
См ответ тыкать в для Как получить выражение между сбалансированными скобками .
Java настраиваемое решение без регулярных выражений
Вот настраиваемое решение, разрешающее использование односимвольных буквенных разделителей в 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;
}
}
Пример использования:
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)))]
Регулярное выражение с использованием Ruby (версия 1.9.3 или выше):
/(?<match>\((?:\g<match>|[^()]++)*\))/
Вам нужны первые и последние скобки. Используйте что-то вроде этого:
str.indexOf ('('); - это даст вам первое вхождение
str.lastIndexOf ( ')'); - последний
Итак, вам нужна строка между,
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"
Ответ зависит от того, нужно ли вам сопоставлять совпадающие наборы скобок или просто от первого открытия до последнего закрытия во входном тексте.
Если вам нужно сопоставить совпадающие вложенные скобки, вам нужно нечто большее, чем регулярные выражения. - см @dehmann
Если это только сначала открыт, чтобы в последний раз увидеть @Zach
Решите, что вы хотите с:
abc ( 123 ( foobar ) def ) xyz ) ghij
Вы должны решить, что ваш код должен соответствовать в этом случае.
Поскольку регулярное выражение js не поддерживает рекурсивное сопоставление, я не могу сделать сопоставление в скобках.
так что это простой javascript для версии цикла, который превращает строку «method (arg)» в массив
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)
результат как
[ { 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' } ]
Хотя многие ответы упоминают об этом в той или иной форме, говоря, что регулярное выражение не поддерживает рекурсивное сопоставление и т. Д., Основная причина этого кроется в корнях теории вычислений.
Язык формы {a^nb^n | n>=0} is not regular
. Regex может соответствовать только вещам, которые являются частью обычного набора языков.
Читать дальше @ здесь
Я не использовал регулярные выражения, так как трудно иметь дело с вложенным кодом. Таким образом, этот фрагмент должен быть в состоянии позволить вам захватывать фрагменты кода со сбалансированными скобками:
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
Я использовал это для извлечения фрагментов кода из текстового файла.
Этот тоже работал
re.findall(r'\(.+\)', s)
Это может быть полезно для некоторых:
Структуры соответствия, такие как:
Здесь вы можете увидеть сгенерированное регулярное выражение в действии
/**
* 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;
};
Это не в полной мере отвечает на вопрос OP, но я думаю, что это может быть полезным для некоторых, приходящих сюда для поиска регулярных выражений вложенной структуры.