use strict; use warnings; my @toTest = ( "Cont(ains balanced( nested Br(ack)ets )in t)he text", "Con(tains i(mbalan(ced Br(ack)ets, )one c)lose missing", "Contains i(mbalan(ced Br(ack)ets, )one op)en m)missing", "No brackets in this string", "Won)ky br(ackets in) this s(tring", "More wonky br(ackets in) th)is s(tring", "Just the one( leading bracket", "And just th)e one trailing bracket", "So(me m(ultip)le n(est(s in) thi)s o)ne", "Ther(e is( mo(re) de(e)p )nes(ti(n(g i)n (mul)ti)p(l)es) he)re", "Th(er(e is( mo(re) de(e)p )nes(ti(n(g i)n (mul)ti)p(l)es) he)re", "Ther(e is( mo(re) de(e)p )nes(ti(n(g i)n (mul)ti)p(l)es) he)r)e", "Ther(e is( mo(re) de(e)p )n(es(ti(n(g i)n (mul)ti)p(l)es) he)re", "Some d((oub)le b)rackets", "(Some d((oub)le b)rackets)", "ab(())cde", "ab(c(d)e", "ab(c)d)e", "ab(c)de", ); my @memoList; my $rxNest; $rxNest = qr {(?x) ( \( [^()]* (?: (??{$rxNest}) [^()]* )* \) ) (?{ [ @{$^R}, $^N ] }) }; my $rxOnlyNested; { use re q(eval); $rxOnlyNested = qr {(?x) (?{ [] }) ^ [^()]* (?: $rxNest [^()]* )+ \z (?{ @memoList = @{$^R} }) }; } testString($_) for @toTest; sub testString { my $string = shift; @memoList = (); print "\nString: $string\n"; if($string =~ /$rxOnlyNested/) { print " Match succeeded\n"; print " ---------------\n"; print " Before brackets:-\n"; print " -->@{[substr $string, 0, $-[1]]}<--\n"; print " Bracket pairs:-\n"; print " $_\n" for @memoList; print " After brackets:-\n"; print " -->@{[substr $string, $+[1]]}<--\n"; } else { print " Match failed\n"; } } #### String: Cont(ains balanced( nested Br(ack)ets )in t)he text Match succeeded --------------- Before brackets:- -->Cont<-- Bracket pairs:- (ack) ( nested Br(ack)ets ) (ains balanced( nested Br(ack)ets )in t) After brackets:- -->he text<-- String: Con(tains i(mbalan(ced Br(ack)ets, )one c)lose missing Match failed String: Contains i(mbalan(ced Br(ack)ets, )one op)en m)missing Match failed String: No brackets in this string Match failed String: Won)ky br(ackets in) this s(tring Match failed String: More wonky br(ackets in) th)is s(tring Match failed String: Just the one( leading bracket Match failed String: And just th)e one trailing bracket Match failed String: So(me m(ultip)le n(est(s in) thi)s o)ne Match succeeded --------------- Before brackets:- -->So<-- Bracket pairs:- (ultip) (s in) (est(s in) thi) (me m(ultip)le n(est(s in) thi)s o) After brackets:- -->ne<-- String: Ther(e is( mo(re) de(e)p )nes(ti(n(g i)n (mul)ti)p(l)es) he)re Match succeeded --------------- Before brackets:- -->Ther<-- Bracket pairs:- (re) (e) ( mo(re) de(e)p ) (g i) (mul) (n(g i)n (mul)ti) (l) (ti(n(g i)n (mul)ti)p(l)es) (e is( mo(re) de(e)p )nes(ti(n(g i)n (mul)ti)p(l)es) he) After brackets:- -->re<-- String: Th(er(e is( mo(re) de(e)p )nes(ti(n(g i)n (mul)ti)p(l)es) he)re Match failed String: Ther(e is( mo(re) de(e)p )nes(ti(n(g i)n (mul)ti)p(l)es) he)r)e Match failed String: Ther(e is( mo(re) de(e)p )n(es(ti(n(g i)n (mul)ti)p(l)es) he)re Match failed String: Some d((oub)le b)rackets Match succeeded --------------- Before brackets:- -->Some d<-- Bracket pairs:- (oub) ((oub)le b) After brackets:- -->rackets<-- String: (Some d((oub)le b)rackets) Match succeeded --------------- Before brackets:- --><-- Bracket pairs:- (oub) ((oub)le b) (Some d((oub)le b)rackets) After brackets:- --><-- String: ab(())cde Match succeeded --------------- Before brackets:- -->ab<-- Bracket pairs:- () (()) After brackets:- -->cde<-- String: ab(c(d)e Match failed String: ab(c)d)e Match failed String: ab(c)de Match succeeded --------------- Before brackets:- -->ab<-- Bracket pairs:- (c) After brackets:- -->de<--