# Regexp/scan question

**URL:** <https://www.ruby-forum.com/t/regexp-scan-question/78987>\
**Category:** Ruby\
**Created:** [December 11, 2006, 10:29am UTC](https://www.ruby-forum.com/t/regexp-scan-question/78987 "2006-12-11T10:29:07Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [December 11, 2006, 10:29am UTC](https://www.ruby-forum.com/t/regexp-scan-question/78987/1 "2006-12-11T10:29:07Z")

</div>

Hello,

I need to match a chunk of code like this:

…  
…  
#begin here  
…}  
…end  
…}  
…}  
…end  
…  
…

I need to match from “the #begin here” up to the n-th closing token  
(i.e. ‘}’ or ‘end’). n can be arbitrary (let’s consider that it is  
meaningful, i.e. there are no more ‘}’ + 'end’s than n.

Example  
match\_stuff(2):

#begin here  
…}  
…end

match\_stuff(4):

#begin here  
…}  
…end  
…}  
…}

etc.

What’s the most optimal way to accomplish this? I have been trying with  
scan() but I did not really succeed yet

TIA,  
Peter

\_\_  
[http://www.rubyrailways.com](http://www.rubyrailways.com)

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [December 11, 2006, 10:39am UTC](https://www.ruby-forum.com/t/regexp-scan-question/78987/2 "2006-12-11T10:39:07Z")

</div>

Peter S. wrote:

> …}  
> …end  
> …  
> …
> 
> I need to match from “the #begin here” up to the n-th closing token  
> (i.e. ‘}’ or ‘end’). n can be arbitrary (let’s consider that it is  
> meaningful, i.e. there are no more ‘}’ + 'end’s than n.

n = 4  
text =~ /#begin(.\*(}|end)){#{n}}/m

?

(not tested).

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [December 11, 2006, 10:45am UTC](https://www.ruby-forum.com/t/regexp-scan-question/78987/3 "2006-12-11T10:45:08Z")

</div>

Carlos wrote:

> > …end
> 
> n = 4  
> text =~ /#begin(.\*?(}|end)){#{n}}/m  
> ^  
> better with ‘?’, to make it not greedy :).

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [December 11, 2006, 10:51am UTC](https://www.ruby-forum.com/t/regexp-scan-question/78987/4 "2006-12-11T10:51:11Z")

</div>

Carlos wrote:

> > …end  
> > n = 4  
> > text =~ /#begin(.\*(}|end)){#{n}}/m

Sorry, I need to ‘scan’ it. I have been playing around with similar  
regexps, but they did not work out. E.g. also yours:

irb(main):007:0\> text = ‘… #begin aaaa end bbb } ccc end ddd’  
=\> “… #begin aaaa end bbb } ccc end ddd”  
irb(main):008:0\> n = 2  
=\> 2  
irb(main):009:0\> text.scan(/#begin(.\*(}|end)){#{n}}/m)  
=\> [[" ccc end", “end”]]

does not work with scan…

Cheers,  
Peter

\_\_  
[http://www.rubyrailways.com](http://www.rubyrailways.com)

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [December 11, 2006, 10:57am UTC](https://www.ruby-forum.com/t/regexp-scan-question/78987/5 "2006-12-11T10:57:13Z")

</div>

> IMHO this does not work because of the greedy “._". You could try with  
> reluctant, i.e. "._?”. Also the grouping does not catch the whole  
> sequence.

Yeah, I tried to correct these problems but I am still not quite  
there…

Carlos’ regexp, vol 2 (with greedy ?)

irb(main):007:0\> text = ‘… #begin aaaa end bbb } ccc end ddd’  
=\> “… #begin aaaa end bbb } ccc end ddd”  
irb(main):008:0\> n = 2  
=\> 2  
irb(main):009:0\> text.scan(/#begin(.\*?(}|end)){#{n}}/m)  
=\> [[" ccc end", “end”]]

And I would like to get

[[“#begin aaaa end bbb }”]]

OK, I know that I did not specify the problem correctly for the first  
time, maybe now it is more clear…

Cheers,  
Peter

\_\_  
[http://www.rubyrailways.com](http://www.rubyrailways.com)

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [December 11, 2006, 10:51am UTC](https://www.ruby-forum.com/t/regexp-scan-question/78987/6 "2006-12-11T10:51:11Z")

</div>

On 11.12.2006 10:37, Carlos wrote:

> > …end  
> > n = 4  
> > text =~ /#begin(.\*(}|end)){#{n}}/m
> 
> ?
> 
> (not tested).

IMHO this does not work because of the greedy “._". You could try with  
reluctant, i.e. "._?”. Also the grouping does not catch the whole  
sequence.

```
robert
```

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [December 11, 2006, 11:03am UTC](https://www.ruby-forum.com/t/regexp-scan-question/78987/7 "2006-12-11T11:03:14Z")

</div>

Peter S. wrote:

> > > #begin here  
> > > meaningful, i.e. there are no more ‘}’ + 'end’s than n.  
> > > =\> “… #begin aaaa end bbb } ccc end ddd”  
> > > irb(main):008:0\> n = 2  
> > > =\> 2  
> > > irb(main):009:0\> text.scan(/#begin(.\*(}|end)){#{n}}/m)  
> > > =\> [[" ccc end", “end”]]
> 
> does not work with scan…

To make it work with scan just make the parens non-capturing:

irb(main):001:0\> text = “#begin aaa end bbb } ccc } #begin ddd end eee  
end fff”  
=\> “#begin aaa end bbb } ccc } #begin ddd end eee end fff”  
irb(main):002:0\> text.scan(/#begin(?:.\*?(?:}|end)){2}/m)  
=\> ["#begin aaa end bbb }", “#begin ddd end eee end”]

Good luck.

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [December 11, 2006, 11:13am UTC](https://www.ruby-forum.com/t/regexp-scan-question/78987/8 "2006-12-11T11:13:15Z")

</div>

> To make it work with scan just make the parens non-capturing:
> 
> irb(main):001:0\> text = “#begin aaa end bbb } ccc } #begin ddd end eee  
> end fff”  
> =\> “#begin aaa end bbb } ccc } #begin ddd end eee end fff”  
> irb(main):002:0\> text.scan(/#begin(?:.\*?(?:}|end)){2}/m)  
> =\> [“#begin aaa end bbb }”, “#begin ddd end eee end”]

Ha! That was the trick I have been looking for! Muchas Gracias, Carlos.

Cheers,  
Peter

\_\_  
[http://www.rubyrailways.com](http://www.rubyrailways.com)

---

<div class="post-metadata">

**Author:** ![splattael](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@splattael](https://www.ruby-forum.com/u/splattael)\
**Post date:** [December 11, 2006, 11:25am UTC](https://www.ruby-forum.com/t/regexp-scan-question/78987/9 "2006-12-11T11:25:16Z")

</div>

Peter S. wrote:

> …}  
> …end

This won’t solve the entire problem, but it will give you an array of  
indices to matching elements:

* * *

#!/usr/bin/ruby -w

data = File.read(“testdata.txt”)

match\_indices = []

data.scan(/}/) do  
match\_indices \<\< Regexp.last\_match.begin(0)  
end

puts match\_indices

* * *

You could begin by scanning to your planned start mark, then scan for  
matching elements using this code. Or you could segregate the block  
between  
the start and end marks, then scan for matches in the substring using  
this  
code.
