# \[Regex\] Alternative for look behind

**URL:** <https://www.ruby-forum.com/t/regex-alternative-for-look-behind/146019>\
**Category:** Ruby\
**Created:** [August 16, 2008, 3:59am UTC](https://www.ruby-forum.com/t/regex-alternative-for-look-behind/146019 "2008-08-16T03:59:24Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![janus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/c2a13f/32.png) [@janus](https://www.ruby-forum.com/u/janus)\
**Post date:** [August 16, 2008, 3:59am UTC](https://www.ruby-forum.com/t/regex-alternative-for-look-behind/146019/1 "2008-08-16T03:59:24Z")

</div>

Hi all,

I need to replace all ‘-’ characters at the beginning of a sequence  
(string) with 'X’s (until a character appears that is not ‘-’).

E.g.

-------abcde-gh----

should be changed into

XXXXXXXabcde-gh----

If Ruby would support look behind in Regular Expressions, I could  
probably do something like this:

sequence.gsub(/(?=^-\*)-/, “X”)

But unfortunately Ruby does not support look behind.

Of course, I could count the number of ‘-’ at the beginning of the  
sequence:

x = sequence[/-\*/].length

And then I could replace the first x characters with “X”. But I don’t  
like that solution, it feels clumsy. Is there an elegant way of doing  
this?

Thanks in advance & best regards!  
Janus

---

<div class="post-metadata">

**Author:** ![janus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/c2a13f/32.png) [@janus](https://www.ruby-forum.com/u/janus)\
**Post date:** [August 16, 2008, 4:49am UTC](https://www.ruby-forum.com/t/regex-alternative-for-look-behind/146019/2 "2008-08-16T04:49:53Z")

</div>

From: Janus B. [[mailto:removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)]

# I need to replace all ‘-’ characters at the beginning of a sequence

# (string) with 'X’s (until a character appears that is not ‘-’).

# E.g.

# -------abcde-gh----

# should be changed into

# XXXXXXXabcde-gh----

my initial reaction was not a look behind,

irb(main):062:0\> s.gsub(/(^-\*)/){$1.tr(“-”,“X”)}  
=\> “XXXXXXXabcde-gh----”

the second was to use oniguruma,

re=Oniguruma::ORegexp.new( ‘(?^-_)(?._)’ )  
#=\> /(?^-_)(?._)/  
s  
#=\> “-------abcde-gh----”  
m=re.match s  
#=\> #MatchData:0x2c51b7c  
m[:dashes]  
#=\> “-------”  
m[:after]  
#=\> “abcde-gh----”  
…

nice naming feature, but overkill for this case. ymmv.

kind regards -botp

---

<div class="post-metadata">

**Author:** ![janus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/c2a13f/32.png) [@janus](https://www.ruby-forum.com/u/janus)\
**Post date:** [August 16, 2008, 11:40am UTC](https://www.ruby-forum.com/t/regex-alternative-for-look-behind/146019/3 "2008-08-16T11:40:25Z")

</div>

On 16.08.2008 03:56, Janus B. wrote:

> If Ruby would support look behind in Regular Expressions, I could  
> probably do something like this:
> 
> sequence.gsub(/(?=^-\*)-/, “X”)
> 
> But unfortunately Ruby does not support look behind.

It does - in 1.9. But:

irb(main):001:0\> sequence = ‘-------abcde-gh----’  
=\> “-------abcde-gh----”  
irb(main):002:0\> sequence.gsub(/(?=^-\*)-/, “X”)  
=\> “X------abcde-gh----”  
irb(main):003:0\>

I am not sure lookbehind is the proper means here. After all you want  
to replace a sequence of dashes _before_ a particular sequence  
(according to your description above). So you would rather use  
lookforward, wouldn’t you?

irb(main):003:0\> sequence.gsub(/-(?=-\*abcde)/, “X”)  
=\> “XXXXXXXabcde-gh----”

Granted, it does not anchor.

Here’s another solution

irb(main):008:0\> s = sequence.dup  
=\> “-------abcde-gh----”  
irb(main):009:0\> while s.sub! /^(X\*)-/, ‘\1X’; end  
=\> nil  
irb(main):010:0\> s  
=\> “XXXXXXXabcde-gh----”

But I’d rather use Pena’s solution or another block form

irb(main):011:0\> sequence.sub(/^-_/) {|m| “X” \* m.length}  
=\> “XXXXXXXabcde-gh----”  
irb(main):012:0\> sequence.sub(/^-_/) {|m| m.tr ‘-’,‘X’}  
=\> “XXXXXXXabcde-gh----”

Kind regards

robert

---

<div class="post-metadata">

**Author:** ![janus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/c2a13f/32.png) [@janus](https://www.ruby-forum.com/u/janus)\
**Post date:** [August 17, 2008, 12:55pm UTC](https://www.ruby-forum.com/t/regex-alternative-for-look-behind/146019/4 "2008-08-17T12:55:52Z")

</div>

On 16.08.2008 18:46, Janus B. wrote:

> Thanks to both of you! I like your solutions. I’m a fan of one liners…

You’re welcome!

> write the equivalent replacing characters at the end of the string using  
> look ahead it works just fine:
> 
> irb(main):001:0\> sequence = ‘-------abcde-gh----’  
> =\> “-------abcde-gh----”  
> irb(main):002:0\> sequence.gsub(/-(?=-\*$)/, “X”)  
> =\> “-------abcde-ghXXXX”

I believe the reason is that with lookbehind the regular expression  
needs start matching at the _same_ location (the beginning of the  
sequence) multiple times. Even though lookbehind does not consume  
characters I believe RX implementations prohibit matching at the same  
location over and over again - partly for efficiency reasons but also to  
avoid endless loops. The lookforward solution with replacement at the  
end of the sequence moves the start position of the match one character  
forward for every match. That’s how I explain myself why the straight  
forward lookbehind does not work.

There’s another reason, why lookbehind won’t work: the docs state

“Subexp of look-behind must be fixed character length.”

See [サービス終了のお知らせ](http://www.geocities.jp/kosako3/oniguruma/doc/RE.txt)

At the moment I cannot think of a solution with lookbehind that would  
avoid these issues because all lookbehinds must start matching at the  
beginning of the sequence in order to fulfill your requirement that the  
initial sequence must be replaced.

> The problem with look ahead/forward is, that I have to make sure the  
> sequence starts with “-”. Otherwise something like this could happen:
> 
> irb(main):003:0\> sequence = ‘abcde-gh----’  
> =\> “abcde-gh----”  
> irb(main):004:0\> sequence.gsub(/-(?=-\*[abcdefgh])/, “X”)  
> =\> “abcdeXgh----”

Well, this won’t happen if the sequence “abcde” is known to appear only  
after an initial sequence of dashes. (Note the difference between your  
lookforward solution and mine: you created a character class while I  
just matched the plain sequence). But of course it’s not the same as  
“replace initial portion of dashes”.

I can recommend “Mastering regular expressions” if you want to dive  
deeper into this: it’s pretty well written and does only have as much  
theory of regular languages as necessary but explains very well  
differences between regular expression implementations and also  
considers efficiency aspects.

> **[Mastering Regular Expressions, 3rd Edition](https://www.oreilly.com/library/view/mastering-regular-expressions/0596528124/)**
>
> Regular expressions are an extremely powerful tool for manipulating text and data. They are now standard features in a wide range of languages and popular tools, including Perl, Python, Ruby, … - Selection from Mastering Regular Expressions, 3rd...

Kind regards

robert

---

<div class="post-metadata">

**Author:** ![janus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/c2a13f/32.png) [@janus](https://www.ruby-forum.com/u/janus)\
**Post date:** [August 16, 2008, 6:48pm UTC](https://www.ruby-forum.com/t/regex-alternative-for-look-behind/146019/5 "2008-08-16T18:48:42Z")

</div>

Thanks to both of you! I like your solutions. I’m a fan of one liners…

I ended up using this one, as it has the best readability for a novice  
programmer like me imho:

Robert K. wrote:

> irb(main):011:0\> sequence.sub(/^-\*/) {|m| “X” \* m.length}  
> =\> “XXXXXXXabcde-gh----”

Robert K. wrote:

> It does - in 1.9. But:
> 
> irb(main):001:0\> sequence = ‘-------abcde-gh----’  
> =\> “-------abcde-gh----”  
> irb(main):002:0\> sequence.gsub(/(?=^-\*)-/, “X”)  
> =\> “X------abcde-gh----”

Thanks for the info. Just downloaded 1.9 to check it out. But I still  
don’t comprehend why look behind is not working like I expected. If I  
write the equivalent replacing characters at the end of the string using  
look ahead it works just fine:

irb(main):001:0\> sequence = ‘-------abcde-gh----’  
=\> “-------abcde-gh----”  
irb(main):002:0\> sequence.gsub(/-(?=-\*$)/, “X”)  
=\> “-------abcde-ghXXXX”

Robert K. wrote:

> I am not sure lookbehind is the proper means here. After all you want  
> to replace a sequence of dashes _before_ a particular sequence  
> (according to your description above). So you would rather use  
> lookforward, wouldn’t you?
> 
> irb(main):003:0\> sequence.gsub(/-(?=-\*abcde)/, “X”)  
> =\> “XXXXXXXabcde-gh----”
> 
> Granted, it does not anchor.

The problem with look ahead/forward is, that I have to make sure the  
sequence starts with “-”. Otherwise something like this could happen:

irb(main):003:0\> sequence = ‘abcde-gh----’  
=\> “abcde-gh----”  
irb(main):004:0\> sequence.gsub(/-(?=-\*[abcdefgh])/, “X”)  
=\> “abcdeXgh----”

Kind regards,  
Janus
