51

How would I return the string between two string markers of a string in Ruby?

For example I have:

  • input_string
  • str1_markerstring
  • str2_markerstring

Want to do something like:

input_string.string_between_markers(str1_markerstring, str2_markerString)

Example text:

s
# => "Charges for the period 2012-01-28 00:00:00 to 2012-02-27 23:59:59:<br>\nAny Network Cap remaining: $366.550<br>International Cap remaining: $0.000"
str1_markerstring
# => "Charges for the period"
str2_markerstring
# => "Any Network Cap"
s[/#{str1_markerstring}(.*?)#{str2_markerstring}/, 1]
# => nil  # IE DIDN'T WORK IN THIS CASE

Using Ruby 1.9.3.

3 Answers 3

101
input_string = "blahblahblahSTARTfoofoofooENDwowowowowo"
str1_markerstring = "START"
str2_markerstring = "END"

input_string[/#{str1_markerstring}(.*?)#{str2_markerstring}/m, 1]
#=> "foofoofoo"

or to put it in a method:

class String
  def string_between_markers marker1, marker2
    self[/#{Regexp.escape(marker1)}(.*?)#{Regexp.escape(marker2)}/m, 1]
  end
end

"blahblahblahSTARTfoofoofooENDwowowowowo".string_between_markers("START", "END")
#=> "foofoofoo"
Sign up to request clarification or add additional context in comments.

6 Comments

As a general idea this is fine, but for the final implementation, one should use Regexp.escape on the markers.
oh, just noted this didn't work for my text - I've put my example text in the question above. It's basically representative of the text I'll get back in a html <div>.
@Grep I edited my answer a while after my first post. The key is to put the option m after the regex. See the difference between my answer now and what you tried? This will make it match beyond a single line. Without it, it doesn't.
ar yes, this works excellent now - thanks for responding sawa
the m modifier enables multiline mode
|
20

Just split it twice and get the string between the markers:

input_string.split("str1_markerstring").last.split("str2_markerstring").first

1 Comment

hells yeah, screw Regex if you can just split it
0

Here's some alternate ways to do what you want, that are how I'd go about it:

s = "Charges for the period 2012-01-28 00:00:00 to 2012-02-27 23:59:59:<br>\nAny Network Cap remaining: $366.550<br>International Cap remaining: $0.000"  # => "Charges for the period 2012-01-28 00:00:00 to 2012-02-27 23:59:59:<br>\nAny Network Cap remaining: $366.550<br>International Cap remaining: $0.000"

dt1, dt2 = /period (\S+ \S+) to (\S+ \S+):/.match(s).captures  # => ["2012-01-28 00:00:00", "2012-02-27 23:59:59"]
dt1                                                            # => "2012-01-28 00:00:00"
dt2                                                            # => "2012-02-27 23:59:59"

This is using "period" and "to" and the trailing ":" to mark the begin and end of the range to be searched for, and grabbing the non-whitespace characters that signify the date and time in each datetime stamp.

Alternately, using "named-captures" predefines the variables:

/period (?<dt1>\S+ \S+) to (?<dt2>\S+ \S+):/ =~ s  # => 16
dt1                                                # => "2012-01-28 00:00:00"
dt2                                                # => "2012-02-27 23:59:59"

From that point, if you want to break down the values returned you could parse them as dates:

require 'date'
d1 = DateTime.strptime(dt1, '%Y-%m-%d %H:%M:%S')  # => #<DateTime: 2012-01-28T00:00:00+00:00 ((2455955j,0s,0n),+0s,2299161j)>
d1.month                                          # => 1
d1.day                                            # => 28

Or you could even use sub-captures:

matches = /period (?<dt1>(?<date1>\S+) (?<time1>\S+)) to (?<dt2>(?<date2>\S+) (?<time2>\S+)):/.match(s)
matches # => #<MatchData "period 2012-01-28 00:00:00 to 2012-02-27 23:59:59:" dt1:"2012-01-28 00:00:00" date1:"2012-01-28" time1:"00:00:00" dt2:"2012-02-27 23:59:59" date2:"2012-02-27" time2:"23:59:59">
matches['dt1']   # => "2012-01-28 00:00:00"
matches['date1'] # => "2012-01-28"
matches['time2'] # => "23:59:59"

This is all documented in the Regexp documentation.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.