# Regex question: this should be easy but doesn't work as I expect

**URL:** <https://rubytalk.org/t/regex-question-this-should-be-easy-but-doesnt-work-as-i-expect/33909>\
**Category:** ruby-talk\
**Created:** [20 December 2006 19:10 UTC](https://rubytalk.org/t/regex-question-this-should-be-easy-but-doesnt-work-as-i-expect/33909 "2006-12-20T19:10:05Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Sam\_Kong](https://avatars.discourse-cdn.com/v4/letter/s/ad7895/32.png) [@Sam\_Kong](https://rubytalk.org/u/Sam_Kong)\
**Post date:** [20 December 2006 19:10 UTC](https://rubytalk.org/t/regex-question-this-should-be-easy-but-doesnt-work-as-i-expect/33909/1 "2006-12-20T19:10:05Z")

</div>

Hi all,

-----Code------

re = [  
&nbsp;&nbsp;/(one).+?(three).+?(five)/,  
&nbsp;&nbsp;/(one).+?(three)?.+?(five)/,  
&nbsp;&nbsp;/(one).+?(three|).+?(five)/,  
&nbsp;&nbsp;/(one).+(three|).+?(five)/  
]

re.each\_with\_index do |r, idx|  
&nbsp;&nbsp;puts idx  
&nbsp;&nbsp;p "one two three four five".scan(r)  
&nbsp;&nbsp;p "one two four five".scan(r)  
end

-----Result---------

0  
[["one", "three", "five"]]  
[]  
1  
[["one", nil, "five"]]  
[["one", nil, "five"]]  
2  
[["one", "", "five"]]  
[["one", "", "five"]]  
3  
[["one", "", "five"]]  
[["one", "", "five"]]

> **···**
>
> -----------------
> 
> All regexes failed my expectation.
> 
> What I want is
> 
> "one two three four five" #=\> [["one", "three", "five']]  
> "one two four five" #=\> [["one", nil, "five']]
> 
> In short, in the string, "three" might or might not exist.  
> What regex can match for both?
> 
> Thanks.
> 
> Sam

---

<div class="post-metadata">

**Author:** ![W\_James](https://avatars.discourse-cdn.com/v4/letter/w/e274bd/32.png) [@W\_James](https://rubytalk.org/u/W_James)\
**Post date:** [20 December 2006 19:55 UTC](https://rubytalk.org/t/regex-question-this-should-be-easy-but-doesnt-work-as-i-expect/33909/2 "2006-12-20T19:55:08Z")

</div>

Sam Kong wrote:

> Hi all,
> 
> -----Code------
> 
> re = [  
> &nbsp;&nbsp;/(one).+?(three).+?(five)/,  
> &nbsp;&nbsp;/(one).+?(three)?.+?(five)/,  
> &nbsp;&nbsp;/(one).+?(three|).+?(five)/,  
> &nbsp;&nbsp;/(one).+(three|).+?(five)/  
> ]
> 
> re.each\_with\_index do |r, idx|  
> &nbsp;&nbsp;puts idx  
> &nbsp;&nbsp;p "one two three four five".scan(r)  
> &nbsp;&nbsp;p "one two four five".scan(r)  
> end
> 
> -----Result---------
> 
> 0  
> [["one", "three", "five"]]  
>   
> 1  
> [["one", nil, "five"]]  
> [["one", nil, "five"]]  
> 2  
> [["one", "", "five"]]  
> [["one", "", "five"]]  
> 3  
> [["one", "", "five"]]  
> [["one", "", "five"]]
> 
> -----------------
> 
> All regexes failed my expectation.
> 
> What I want is
> 
> "one two three four five" #=\> [["one", "three", "five']]  
> "one two four five" #=\> [["one", nil, "five']]
> 
> In short, in the string, "three" might or might not exist.  
> What regex can match for both?
> 
> Thanks.
> 
> Sam

&nbsp;&nbsp;/(one) two (?:(three) )?four (five)/

---

<div class="post-metadata">

**Author:** ![Suraj\_Kurapati1](https://avatars.discourse-cdn.com/v4/letter/s/f19dbf/32.png) [@Suraj\_Kurapati1](https://rubytalk.org/u/Suraj_Kurapati1)\
**Post date:** [20 December 2006 20:07 UTC](https://rubytalk.org/t/regex-question-this-should-be-easy-but-doesnt-work-as-i-expect/33909/3 "2006-12-20T20:07:10Z")

</div>

Sam Kong wrote:

> What I want is
> 
> "one two three four five" #=\> [["one", "three", "five']]  
> "one two four five" #=\> [["one", nil, "five']]
> 
> In short, in the string, "three" might or might not exist.  
> What regex can match for both?

/one|three|five/ can. Although, its result is not exactly in the form  
you want:

> > "one two three four five".scan /one|three|five/

=\> ["one", "three", "five"]

> > "one two four five".scan /one|three|five/

=\> ["one", "five"]

> **···**
>
> --  
> Posted via [http://www.ruby-forum.com/\](http://www.ruby-forum.com/%5C).

---

<div class="post-metadata">

**Author:** ![Carlos](https://avatars.discourse-cdn.com/v4/letter/c/ecae2f/32.png) [@Carlos](https://rubytalk.org/u/Carlos)\
**Post date:** [20 December 2006 23:02 UTC](https://rubytalk.org/t/regex-question-this-should-be-easy-but-doesnt-work-as-i-expect/33909/4 "2006-12-20T23:02:53Z")

</div>

[Sam Kong \<sam.s.kong@gmail.com\>, 2006-12-20 20.10 CET]

> What I want is
> 
> "one two three four five" #=\> [["one", "three", "five']]  
> "one two four five" #=\> [["one", nil, "five']]
> 
> In short, in the string, "three" might or might not exist.  
> What regex can match for both?

Hi. The problem is that you can very easily NOT match "three" even if it's  
there. I mean, if you have  
&nbsp;&nbsp;&nbsp;/1.\*3?.\*5/ =~ '12345'

the engine can succeed matching the 1 at the beginning, the 5 at the end,  
and trying to match the 3 where the 4 is... and failing, but since it's  
optional, the overall match succeeds.

I think you should try it in two steps: first, try to match with the 3; if  
that fails, without the "3". Something like:

/(?:(1).\*(3)|(1)).\*(5)/

(The '1' will come either on the first or third array position, you'll have  
to take care of that.)

Maybe there is a simpler solution, but it doesn't come to my mind.

Good luck.

> **···**
>
> --

---

<div class="post-metadata">

**Author:** ![Simon\_Strandgaard2](https://avatars.discourse-cdn.com/v4/letter/s/ac91a4/32.png) [@Simon\_Strandgaard2](https://rubytalk.org/u/Simon_Strandgaard2)\
**Post date:** [20 December 2006 23:46 UTC](https://rubytalk.org/t/regex-question-this-should-be-easy-but-doesnt-work-as-i-expect/33909/5 "2006-12-20T23:46:40Z")

</div>

[snip]

> "one two three four five" #=\> [["one", "three", "five']]  
> "one two four five" #=\> [["one", nil, "five']]

[snip]

how about?

irb(main):001:0\> r = /(one) (?: (.\*?three) | ((?:.(?!\>three))\*) ) \*? (five)/x  
=\> /(one) (?: (.\*?three) | ((?:.(?!\>three))\*) ) \*? (five)/x  
irb(main):002:0\> "one two three four five".scan(r)  
=\> [["one", " two three", " four ", "five"]]  
irb(main):003:0\> "one two four five".scan(r)  
=\> [["one", nil, " two four ", "five"]]

> **···**
>
> On 12/20/06, Sam Kong \<sam.s.kong@gmail.com\> wrote:
> 
> --  
> Simon Strandgaard  
> [http://opcoders.com/](http://opcoders.com/)

---

<div class="post-metadata">

**Author:** ![Sam\_Kong](https://avatars.discourse-cdn.com/v4/letter/s/ad7895/32.png) [@Sam\_Kong](https://rubytalk.org/u/Sam_Kong)\
**Post date:** [20 December 2006 21:10 UTC](https://rubytalk.org/t/regex-question-this-should-be-easy-but-doesnt-work-as-i-expect/33909/6 "2006-12-20T21:10:05Z")

</div>

Hi William,

William James wrote:

> &nbsp;&nbsp;/(one) two (?:(three) )?four (five)/

I simplified the actual problem.  
I guess the simplification did not interpret my problem well.

I was parsing html source into price, image, description, etc.  
The image is sometimes missing.

In the example, let's assume that "two" and "four" are arbiturary text.  
So the text might be "...one...three...five" where "..." means some  
arbiturary text.  
If "three" is missing, it will be "...one.....five...".

Can you reconsider the problem please?

Sam

---

<div class="post-metadata">

**Author:** ![Sam\_Kong](https://avatars.discourse-cdn.com/v4/letter/s/ad7895/32.png) [@Sam\_Kong](https://rubytalk.org/u/Sam_Kong)\
**Post date:** [21 December 2006 01:14 UTC](https://rubytalk.org/t/regex-question-this-should-be-easy-but-doesnt-work-as-i-expect/33909/7 "2006-12-21T01:14:48Z")

</div>

Hi Carlos,

Carlos wrote:

> Hi. The problem is that you can very easily NOT match "three" even if it's  
> there. I mean, if you have  
> &nbsp;&nbsp;&nbsp;/1.\*3?.\*5/ =~ '12345'
> 
> the engine can succeed matching the 1 at the beginning, the 5 at the end,  
> and trying to match the 3 where the 4 is... and failing, but since it's  
> optional, the overall match succeeds.
> 
> I think you should try it in two steps: first, try to match with the 3; if  
> that fails, without the "3". Something like:
> 
> /(?:(1).\*(3)|(1)).\*(5)/
> 
> (The '1' will come either on the first or third array position, you'll have  
> to take care of that.)

Yes, you understand exactly what my problem is.  
Actually I guessed it as you said even if I couldn't explain it as well  
as you did.  
The solution I found was using 2 regexes.  
First, I try to find a match assuming "three" is there.  
If it fails, I try to find a match without "three".  
This solved my problem.  
But I wanted to know that if there's a one-shot solution.

This is the actual problem, just in case someone wants to know.

html = \<\<END  
\<tr id="row2\_210819526"\>  
&nbsp;&nbsp;&nbsp;&nbsp;\<td class="year"\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<h5\>2004 Used\</h5\>\</td\>  
&nbsp;&nbsp;&nbsp;&nbsp;\<td class="carlink"\>\<h5\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<a name="210819526" href="210819526.html"\>BMW 325Ci  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Coupe\</a\>\<br /\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</h5\>\</td\>

&nbsp;&nbsp;&nbsp;&nbsp;\<td class="mileage"\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<span class="body20"\>38,604\<br /\>\</span\>\<span  
class="body30"\>Mileage\</span\>\</td\>  
&nbsp;&nbsp;&nbsp;&nbsp;\<td class="price"\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<span class="body20"\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$24,995

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<br /\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</span\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<span class="body30"\>Price\</span\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</td\>

&nbsp;&nbsp;&nbsp;&nbsp;\<td class="distanceFromZip"\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<div class="zip"\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<span class="body20"\>0 mi\<br /\>\</span\>\<span  
class="body30"\>from ZIP\</span\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</div\>\</td\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<td class="productTileCell" rowspan="2" valign="top"\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<div class="srlProductContainer"\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</div\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</td\>

\</tr\>  
\<tr id="row3\_210819526"\>  
&nbsp;&nbsp;&nbsp;&nbsp;\<td class="left"\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<a href=210819526.html\>\<img  
src="[http://images.autotrader.com/images/2006/10/16/210/819/1092478286.210819526.IM1.MAIN.60x45\_A.60x45.jpg&quot](http://images.autotrader.com/images/2006/10/16/210/819/1092478286.210819526.IM1.MAIN.60x45_A.60x45.jpg&quot);  
border="0" bordercolor="#000000" width="60" height="45"\>\</a\>&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<div class="body40" style="padding-bottom:3px"\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<img  
src="[Autotrader - page unavailable](http://www.autotrader.com/img/fyc/icn_camera_17x17.gif&quot);  
alt="Actual Photo Available" width="17" height="17" border="0"  
/\>&nbsp;9 Photos  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<br /\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</div\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<img src="[Autotrader - page unavailable](http://www.autotrader.com/img/blank_dot.gif&quot);  
width="60" height="1" /\>\</td\>  
&nbsp;&nbsp;&nbsp;&nbsp;\<td class="center" colspan="2"\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<div class="centerinfo"\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<p class="color body20"\>Color - Mystic Blue  
Metallic\</p\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<p class="description"\>Dark Blue/Beige, Premium Pkg,  
Xenon Light, Single Compact Disc, Dual Power Seats, Memory Seat, Still  
under Free BMW Maintenance and 4yr/50k Factory...\</p\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<p class="vin"\>VIN WBABV13454JT20104\</p\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<div class="body40" style="padding-top:5px;"\>\<a  
name="210819526" href="210819526.html"\>View Car Details\</a\>\<br /\>\</div\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</div\>\</td\>  
&nbsp;&nbsp;&nbsp;&nbsp;\<td\>&nbsp;\</td\>  
&nbsp;&nbsp;&nbsp;&nbsp;\<td valign="top" class="right body30"\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<p class="dealername"\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<a name="210819526" href="210819526.html"\>null\</a\>  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<br /\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</p\>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<br /\>

&nbsp;&nbsp;&nbsp;&nbsp;\</td\>  
\</tr\>  
END

def parse\_row row  
&nbsp;&nbsp;m = row.scan(/.+?\<h5\>(\d{4}) Used\<\/h5\>.+?\<h5\>.+?\<a name=\"\d+\"  
href=\"(\d+.html)\"\>(.+?)\<\/a\>\<br \/\>.+?\<\/h5\>.+?\<span  
class=\"body20\"\>([0-9,]+)\<br \/\>\<\/span\>\<span  
class=\"body30\"\>Mileage\<\/span\>.+?(\$[0-9,]+).+?(http:\/\/[^\"]+?\.jpg).+?Color  
- (.+?)\<\/p\>/m)  
&nbsp;&nbsp;if m[0].nil?  
&nbsp;&nbsp;&nbsp;&nbsp;m = row.scan(/.+?\<h5\>(\d{4}) Used\<\/h5\>.+?\<h5\>.+?\<a name=\"\d+\"  
href=\"(\d+.html)\"\>(.+?)\<\/a\>\<br \/\>.+?\<\/h5\>.+?\<span  
class=\"body20\"\>([0-9,]+)\<br \/\>\<\/span\>\<span  
class=\"body30\"\>Mileage\<\/span\>.+?(\$[0-9,]+).+?(http:\/\/[^\"]+?\.jpg)?.+?Color  
- (.+?)\<\/p\>/m)  
&nbsp;&nbsp;end  
&nbsp;&nbsp;m[0]  
end

p parse\_row(html)

Sorry about the messy code.

Thanks.

Sam

---

<div class="post-metadata">

**Author:** ![W\_James](https://avatars.discourse-cdn.com/v4/letter/w/e274bd/32.png) [@W\_James](https://rubytalk.org/u/W_James)\
**Post date:** [20 December 2006 22:10 UTC](https://rubytalk.org/t/regex-question-this-should-be-easy-but-doesnt-work-as-i-expect/33909/8 "2006-12-20T22:10:06Z")

</div>

Sam Kong wrote:

> Hi William,
> 
> William James wrote:
> 
> \> /(one) two (?:(three) )?four (five)/
> 
> I simplified the actual problem.  
> I guess the simplification did not interpret my problem well.
> 
> I was parsing html source into price, image, description, etc.  
> The image is sometimes missing.
> 
> In the example, let's assume that "two" and "four" are arbiturary text.  
> So the text might be "...one...three...five" where "..." means some  
> arbiturary text.  
> If "three" is missing, it will be "...one.....five...".
> 
> Can you reconsider the problem please?
> 
> Sam

This is prolix, but it works:

a,b,c = 'one', 'three', 'five'

[  
&nbsp;&nbsp;"one two three four five",  
&nbsp;&nbsp;"one two four five"  
].each{|s|  
&nbsp;&nbsp;if s =~ /#{a} (.+ )?#{c}/  
&nbsp;&nbsp;&nbsp;&nbsp;if s =~ / #{b} /  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p [a,b,c]  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p [a,nil,c]  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
}
