Reputation: 2120
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
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
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
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