İki dış parantez arasındaki tüm metni seçmek için normal bir ifade gerekir.
Misal: some text(text here(possible text)text(possible text(more text)))end text
Sonuç: (text here(possible text)text(possible text(more text)))
İki dış parantez arasındaki tüm metni seçmek için normal bir ifade gerekir.
Misal: some text(text here(possible text)text(possible text(more text)))end text
Sonuç: (text here(possible text)text(possible text(more text)))
Yanıtlar:
Düzenli ifadeler iş için yanlış araçtır, çünkü iç içe geçmiş yapılarla, yani özyineleme ile uğraşıyorsunuz.
Ama bunu yapmak için basit bir algoritma var, ki bu da önceki bir sorunun cevabında tanımladım .
Bu cevabı çabuk başvurmak için eklemek istiyorum. Güncellemek için çekinmeyin.
Dengeleme gruplarını kullanarak .NET Regex .
\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\)
c
Derinlik sayacı olarak nerede kullanılır.
PCRE yinelemeli bir desen kullanarak .
\((?:[^)(]+|(?R))*+\)
Regex101'de demo ; Veya değişmeden:
\((?:[^)(]*(?R)?)*+\)
Regex101'de demo ; Veya performans için unrolled :
\([^)(]*+(?:(?R)[^)(]*)*+\)
Regex101'de demo ; Desen, (?R)
temsil edilen yapıştırılır (?0)
.
Perl, PHP, Notepad ++, R : perl = DOĞRU , Python : Regex paketi ile (?V1)
Perl davranış.
Alt ifade çağrılarını kullanarak Ruby .
Ruby 2.0 ile \g<0>
tam desen çağırmak için kullanılabilir.
\((?>[^)(]+|\g<0>)*\)
Rubular'da Demo ; Ruby 1.9 yalnızca grup yinelemesini yakalamayı destekler :
(\((?>[^)(]+|\g<1>)*\))
Demo'da Rubular ( Ruby 1.9.3'ten beri atom gruplaması )
JavaScript API :: XRegExp.matchRecursive
XRegExp.matchRecursive(str, '\\(', '\\)', 'g');
JS, Java ve diğer regex lezzetleri 2 seviyeye kadar iç içe geçme olmadan tekrarlanır:
\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)
Regex101'de demo . Desene daha derin yuvalama yapılması gerekir .
Dengesiz parantez üzerinde daha hızlı başarısız olmak için +
nicelik belirtecini düşürün.
Java : @jaytea'nın ileri referanslarını kullanan ilginç bir fikir .
(?>[^)(]+|(?R))*+
aynı şeydir (?:[^)(]+|(?R))*+
. Bir sonraki model için aynı şey. Kaydedilmemiş sürüm hakkında, buraya sahipli bir nicelik belirleyici koyabilirsiniz: [^)(]*+
geri izlemeyi önlemek için (kapanma braketi yoksa).
(...(..)..(..)..(..)..(..)..)
konu dizesinde tekrarlanan grup atomunu (çok sayıda iç içe parantez olduğunda sınırlı bir ilgisi vardır ) yapmak yerine, basit bir yakalamayan grup kullanabilir ve tümünü bir atom grubuna dahil edebilirsiniz: (?>(?:[^)(]+|\g<1>)*)
( bu tam olarak iyelikçi bir niceleyici gibi davranır). Ruby 2.x'te iyelik nicelik belirteci kullanılabilir.
Normal ifade yinelemesini kullanabilirsiniz :
\(([^()]|(?R))*\)
Unrecognized grouping construct
.
[^\(]*(\(.*\))[^\)]*
[^\(]*
dizenin başındaki açılış köşeli ayracı olmayan her şeyle eşleşir (\(.*\))
, köşeli ayraç içine alınmış gerekli alt dizeyi yakalar ve dizenin [^\)]*
sonunda kapatma köşeli ayraç olmayan her şeyi eşleştirir. Bu ifadenin köşeli ayraçları eşleştirmeye çalışmadığını unutmayın; bunun için basit bir ayrıştırıcı ( dehmann'ın cevabına bakınız ) daha uygun olacaktır.
(?<=\().*(?=\))
Eşleşen iki parantez arasında metin seçmek isterseniz, normal ifadelerle şansınız kalmaz. Bu imkansız (*) .
Bu normal ifade, dizenizdeki ilk açılış ile son kapanış parantezleri arasındaki metni döndürür.
(*) Normal ifade motorunuzda dengeleme grupları veya özyineleme gibi özellikler yoksa . Bu özellikleri destekleyen motorların sayısı yavaş yavaş artıyor, ancak hala yaygın olarak mevcut değiller.
Bu cevap, düzenli ifadelerin bu görev için neden doğru araç olmadığının teorik sınırlamasını açıklamaktadır.
Düzenli ifadeler bunu yapamaz.
Normal ifadeler, olarak bilinen bir hesaplama modelini temel alır Finite State Automata (FSA)
. Adından da anlaşılacağı gibi, a FSA
yalnızca geçerli durumu hatırlayabilir, önceki durumlar hakkında hiçbir bilgisi yoktur.
Yukarıdaki şemada, S1 ve S2, S1'in başlangıç ve son adım olduğu iki durumdur. Dize ile denersek 0110
, geçiş şu şekilde olur:
0 1 1 0
-> S1 -> S2 -> S2 -> S2 ->S1
İkinci altındadır Yukarıdaki adımlarda, S2
ayrıştırma sonra yani 01
ait 0110
, FSA önceki hakkında bilgisi bulunmadığını 0
içinde 01
sadece mevcut durumu ve bir sonraki girdi sembolü hatırladığım kadarıyla.
Yukarıdaki problemde, açılış parantezinin numarasını bilmemiz gerekir; bu , bir yerde saklanması gerektiği anlamına gelir . Ancak FSAs
bunu yapamadığından, düzenli bir ifade yazılamaz.
Ancak, bu görevi yerine getirmek için bir algoritma yazılabilir. Algoritmalar genellikle düşer Pushdown Automata (PDA)
. PDA
, bir düzey üzerindedir FSA
. PDA'nın bazı ek bilgileri depolamak için ek bir yığını vardır. PDA'lar yukarıdaki problemi çözmek için kullanılabilir, çünkü ' push
' yığındaki açılış parantezini ' pop
' ve kapanış paranteziyle karşılaştığımızda bunları yapabiliriz. Sonunda yığın boşsa parantez açılır ve parantez kapanır. Aksi halde değil.
Aslında .NET düzenli ifadeleri kullanarak bunu yapmak mümkündür, ancak önemsiz değildir, bu yüzden dikkatlice okuyun.
Burada güzel bir makale okuyabilirsiniz . Ayrıca .NET düzenli ifadelerini de okumanız gerekebilir. Buradan okumaya başlayabilirsiniz .
Köşebentler <>
, kaçmayı gerektirmedikleri için kullanıldı.
Normal ifade şöyle görünür:
<
[^<>]*
(
(
(?<Open><)
[^<>]*
)+
(
(?<Close-Open>>)
[^<>]*
)+
)*
(?(Open)(?!))
>
Bu kesin regex:
\(
(?<arguments>
(
([^\(\)']*) |
(\([^\(\)']*\)) |
'(.*?)'
)*
)
\)
Misal:
input: ( arg1, arg2, arg3, (arg4), '(pip' )
output: arg1, arg2, arg3, (arg4), '(pip'
'(pip'
dize olarak doğru yönetildiğini unutmayın . (regülatörde denendi: http://sourceforge.net/projects/regulator/ )
Bu işe yardımcı olmak için dengeli adlı küçük bir JavaScript kütüphanesi yazdım . Bunu yaparak bunu başarabilirsiniz.
balanced.matches({
source: source,
open: '(',
close: ')'
});
Değiştirmeler bile yapabilirsiniz:
balanced.replacements({
source: source,
open: '(',
close: ')',
replace: function (source, head, tail) {
return head + source + tail;
}
});
İşte daha karmaşık ve etkileşimli bir örnek JSFiddle .
Ekleme Bobble balonun cevap , özyinelemeli yapılar desteklenen diğer regex tatlar vardır.
Lua
Kullanım %b()
( %b{}
/ %b[]
kıvırcık parantez / köşeli parantez için):
for s in string.gmatch("Extract (a(b)c) and ((d)f(g))", "%b()") do print(s) end
( demoya bakınız )Perl6 :
Çakışmayan çoklu dengeli parantez eşleşmeleri:
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))」)
Çakışan birden çok dengeli parantez eşleşmesi:
say "Extract (a(b)c) and ((d)f(g))" ~~ m:ov:g/<&paren_any>/;
# => (「(a(b)c)」 「(b)」 「((d)f(g))」 「(d)」 「(g)」)
Demoya bakınız .
Python re
regex olmayan çözüm
Bkz Poke cevabı için dengeli parantez arasına bir ifade almak nasıl .
Java özelleştirilebilir regex olmayan çözüm
Java'da tek karakterli değişmez sınırlayıcılara izin veren özelleştirilebilir bir çözüm:
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;
}
}
Örnek kullanım:
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 kullanan normal ifade (sürüm 1.9.3 veya üstü):
/(?<match>\((?:\g<match>|[^()]++)*\))/
İlk ve son parantezlere ihtiyacınız var. Bunun gibi bir şey kullanın:
str.indexOf ('('); - size ilk gelişmeyi verecektir
str.lastIndexOf ( ')'); - sonuncusu
Yani arasında bir dizeye ihtiyacınız var,
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"
Yanıt, eşleşen köşeli ayraç kümelerini mi yoksa yalnızca ilk açıklığı giriş metnindeki son kapamaya mı uydurmanız gerektiğine bağlıdır.
Eşleşen iç içe parantezleri eşleştirmeniz gerekiyorsa, normal ifadelerden daha fazlasına ihtiyacınız vardır. - görmek @dehmann
Eğer son kapanışa ilk açıksa bkz. kapanışa açıldıysa .
Ne olmak istediğinize karar verin:
abc ( 123 ( foobar ) def ) xyz ) ghij
Bu durumda kodunuzun neyle eşleşmesi gerektiğine karar vermeniz gerekir.
js regex özyinelemeli eşleşmeyi desteklemediğinden, dengeli parantezlerin eşleştirilmesini sağlayamıyorum.
yani bu, dizi yöntemi için "method (arg)" dizesini diziye dönüştüren basit bir javascripttir
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)
sonuç şöyle
[ { 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' } ]
Bu kadar çok cevap, bir şekilde, normal ifadenin özyinelemeli eşleşmeyi desteklemediğini söyleyerek söylese de, bunun temel nedeni Hesaplama Teorisinin köklerinde yatmaktadır.
Formun dili {a^nb^n | n>=0} is not regular
. Normal ifade yalnızca normal dil grubunun bir parçasını oluşturan şeylerle eşleşebilir.
Daha fazla bilgi için @ buradan
İç içe kod ile uğraşmak zor olduğundan regex kullanmadım. Dolayısıyla bu snippet, kod bölümlerini dengeli köşeli parantezlerle almanıza izin verebilmelidir:
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
Bunu bir metin dosyasından kod parçacıkları ayıklamak için kullandım.
Bu da çalıştı
re.findall(r'\(.+\)', s)
Bu, bazıları için yararlı olabilir:
Aşağıdaki gibi yapıları eşleştirin:
Burada oluşturulan normal ifade eylemini görebilirsiniz
/**
* 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;
};
Bu tamamen OP sorusunu ele almaz ama ben bazı buraya iç içe yapı regexp aramak için yararlı olabilir olsa da.