2つの外側の角かっこの間のすべてのテキストを選択するには、正規表現が必要です。
例: some text(text here(possible text)text(possible text(more text)))end text
結果: (text here(possible text)text(possible text(more text)))
2つの外側の角かっこの間のすべてのテキストを選択するには、正規表現が必要です。
例: some text(text here(possible text)text(possible text(more text)))end text
結果: (text here(possible text)text(possible text(more text)))
回答:
クイックリファレンスとしてこの回答を追加したいと思います。自由に更新してください。
バランスグループを使用した.NET正規表現。
\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\)
どこc
深カウンタとして使用されます。
再帰パターンを使用したPCRE。
\((?:[^)(]+|(?R))*+\)
regex101でのデモ。または代替なし:
\((?:[^)(]*(?R)?)*+\)
regex101でのデモ。またはパフォーマンスのために展開:
\([^)(]*+(?:(?R)[^)(]*)*+\)
regex101でのデモ。(?R)
を表すパターンが貼り付けられ(?0)
ます。
PerlやPHP、メモ帳++、 R:perlの= TRUE、Pythonの:正規表現パッケージで(?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
。
[^\(]*(\(.*\))[^\)]*
[^\(]*
文字列の最初の開き(\(.*\))
角括弧で[^\)]*
はないすべてに一致し、角かっこで囲まれた必要な部分文字列を取得し、文字列の終わりにある閉じ角括弧ではないすべてに一致します。この式は角括弧との一致を試みないことに注意してください。単純なパーサー(dehmannの回答を参照)の方が適しています。
(?<=\().*(?=\))
2つの一致する括弧の間のテキストを選択する場合、正規表現ではうまくいきません。これは不可能です(*)。
この正規表現は、文字列の最初の開き括弧と最後の閉じ括弧の間のテキストを返すだけです。
(*)正規表現エンジンにグループのバランスや再帰などの機能がない場合。このような機能をサポートするエンジンの数は徐々に増加していますが、それらはまだ一般的に利用可能ではありません。
この回答は、正規表現がこのタスクに適したツールではない理由の理論的な制限を説明しています。
正規表現はこれを行うことができません。
正規表現は、と呼ばれるコンピューティングモデルに基づいていFinite State Automata (FSA)
ます。名前が示すように、FSA
は現在の状態のみを記憶でき、以前の状態に関する情報はありません。
上の図では、S1とS2が2つの状態で、S1が開始ステップと最終ステップです。したがって、文字列0110
で試してみると、遷移は次のようになります。
0 1 1 0
-> S1 -> S2 -> S2 -> S2 ->S1
私たちは秒であるときに、上記の手順では、S2
構文解析した後、すなわち01
の0110
、FSAは、以前に関する情報がありません0
で01
、それが唯一の現在の状態と次の入力記号を覚えることができるように。
上記の問題では、左括弧の数を知る必要があります。つまり、どこかに保管する必要があります。しかし、それFSAs
ができないので、正規表現を書くことはできません。
ただし、このタスクを実行するアルゴリズムを作成できます。アルゴリズムは通常、に分類されPushdown Automata (PDA)
ます。PDA
の1つ上のレベルですFSA
。PDAには、追加情報を格納するための追加スタックがあります。PDAは上記の問題を解決するために使用できます。これはpush
、スタック内の' '開き括弧と ' pop
'が閉じ括弧に遭遇するとそれらを解決できるためです。最後にスタックが空の場合は、左括弧と右括弧が一致します。そうでない場合。
これは決定的な正規表現です:
\(
(?<arguments>
(
([^\(\)']*) |
(\([^\(\)']*\)) |
'(.*?)'
)*
)
\)
例:
input: ( arg1, arg2, arg3, (arg4), '(pip' )
output: arg1, arg2, arg3, (arg4), '(pip'
'(pip'
は文字列として正しく管理されていることに注意してください。(規制機関で試した:http : //sourceforge.net/projects/regulator/)
このタスクを支援するために、balancedという小さなJavaScriptライブラリを作成しました。あなたはこうすることでこれを達成できます
balanced.matches({
source: source,
open: '(',
close: ')'
});
交換することもできます:
balanced.replacements({
source: source,
open: '(',
close: ')',
replace: function (source, head, tail) {
return head + source + tail;
}
});
ふきだしバブルの答えに加えて、再帰的構成がサポートされている他の正規表現フレーバーがあります。
ルア
%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
非正規表現ソリューション
括弧内の式を取得する方法については、pokeの回答を参照してください。
Javaのカスタマイズ可能な非正規表現ソリューション
Javaで1文字のリテラル区切り文字を許可するカスタマイズ可能なソリューションを次に示します。
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)))]
"""
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 regexは再帰的な一致をサポートしていないため、括弧の一致を機能させることはできません。
これは「method(arg)」文字列を配列にするループバージョンの単純なjavascriptです。
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
。正規表現は、通常の言語セットの一部を形成するものにのみ一致します。
詳しくはこちら @
ネストされたコードを処理することが難しいため、私は正規表現を使用しませんでした。したがって、このスニペットは、括弧で囲まれたコードのセクションを取得できるようにする必要があります。
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の質問に完全に対処するものではありませんが、ネスト構造の正規表現を検索するためにここに来る人には役立つかもしれませんが。