Emil Eriksson
Emil Eriksson

Reputation: 2120

How can I get full match data for the last match in a string for a regex in Ruby?

Regexp#match(str, index) gives me the first match after index in string which is great for iterating through each match in from left to right. But how can I find the last match before a given index? String#rindex gives the index of the last match but what if I want the full match data?

Example:

/.oo/.rmatch("foo boo zoo")

...should yield...

#<MatchData "zoo">

Upvotes: 1

Views: 332

Answers (3)

dbenhur
dbenhur

Reputation: 20408

You could limit how far into the string the regexp may match by sub-stringing the string.

irb> /.oo/.match("foo boo zoo"[0..-3])
=> #<MatchData "foo">
irb> /.oo/.match("foo boo zoo"[0..-3],3)
=> #<MatchData "boo">
irb> /.oo/.match("foo boo zoo"[3..-3]) # can also express the start with slice
=> #<MatchData "boo">
irb> /.oo/.match("foo boo zoo"[0..-3],5)
=> nil

String#scan will repeatedly apply a regexp returning an Array of all matches, from which you just select the last one.

module RegexpHelper
  def rmatch str, rlimit = -1
    str[0..rlimit].scan(self).last
  end
end

Regexp.send :include, RegexpHelper

/.oo/.rmatch 'foo boo moo'     # => "moo"
/.oo/.rmatch 'foo boo moo', -3 # => "boo"
/.oo/.rmatch 'foo boo moo', 4  # => "foo"

Upvotes: 2

pguardiario
pguardiario

Reputation: 54984

Here's a monkeypatch solution:

class Regexp
  def rmatch str, offset = str.length
    last_match = match str
    while last_match && last_match.offset(0).last < offset
      break unless m = match(str, last_match.offset(0).last)
      last_match = m
    end
    last_match
  end
end

p /.oo/.rmatch("foo boo zoo")
#<MatchData "zoo">

Upvotes: 0

Gene
Gene

Reputation: 46960

You could reverse the string, reverse the regex, and use length(str) - index for the start point.

1.9.3p194 :010 > /oo./.match("foo boo zoo".reverse)[0].reverse
=> "zoo" 

Reversing a regex is simple if the language it represents is really regular. Greediness or lack thereof can lead to edge cases you'd have to think through.

If the regex has a Kleene star, I believe this is the only way to get the job done unless you build your own reverse regex matcher, which is a big project.

Upvotes: -1

Related Questions