t56k
t56k

Reputation: 6981

Regex pattern to see if a string contains a number in a range

I'm trying to match a number between 400 and 499 anywhere in a string. For example, both:

string = "[401] One too three"
string2 = "Yes 450 sir okay"

should match. Both:

string3 = "[123] Test"
string4 = "This is another string"

should fail.

What's the best way to write the regex? I wrote:

string =~ /\d{3}/

to see if the string contains a three digit integer. How can I see if that's within the range?

Upvotes: 3

Views: 2759

Answers (4)

Cary Swoveland
Cary Swoveland

Reputation: 110665

def doit(str, rng)
  str.gsub(/-?\d+/).find { |s| rng.cover?(s.to_i) }
end

doit "[401] One too three", 400..499     #=> "401"
doit "Yes 450 sir okay", 400..499        #=> "450"
doit "Yes -450 sir okay", -499..400      #=> "-450"
doit "[123] Test", 400..499              #=> nil
doit "This is another string", 400..499  #=> nil

Recall that String#gsub returns an enumerator, when used with a single argument and no block. The enumerator merely generates matches and performs no substitutions. I've found a number of situations, as here, where this form of the method can be used to advantage.

if str may contain the representations of multiple integers within the specified range, and all such are desired, simply replace Enumerable#find with Enumerable#select:

"401, 532 and -126".gsub(/-?\d+/).select { |s| (-127..451).cover?(s.to_i) }
  #=> ["401", "-126"]

Upvotes: 6

Simple Lime
Simple Lime

Reputation: 11035

If you don't actually need the number afterwords, and just need to determine yes or no the string contains a number in the range of 400-499, you can:

  1. Check that you are at the beginning of a line, or have a non-digit character followed by
  2. the digit '4' followed by
  3. Any 2 digits followed by
  4. the end of a line or a non-digit character

so you end up with a regex looking something like

regex = /(?:^|\D)4\d{2}(?:\D|$)/

or, by using negative look ahead/look behinds:

regex = /(?<!\d)4\d{2}(?!\d)/

and you need step 1 and 4 above to make rule out numbers such as 1400-1499 and 4000-4999 (and other such numbers with more than 3 digits that have 400-499 somewhere buried in them). You can then make use of String#match? in newer ruby versions to get just a simple boolean:

string.match?(regex)   # => true
string2.match?(regex)  # => true
string3.match?(regex)  # => false
string4.match?(regex)  # => false
"1400".match?(regex)   # => false
"400".match?(regex)    # => true
"4000".match?(regex)   # => false
"[1400]".match?(regex) # => false
"[400]".match?(regex)  # => true
"[4000]".match?(regex) # => false

Fairly simple regex, no need to pull out the match and convert it to an integer if you just need a simple yes or no

Upvotes: 6

Tim Biegeleisen
Tim Biegeleisen

Reputation: 520908

I recommend using a general regex to first extract the number from each line. Then, use a regular script to check the range:

s = "[404] Yes sir okay"
data = s.match(/\[(\d+)\]/)
data.captures
num = data[1].to_i

if (num >= 400 && num < 500)
    print "Match"
else
    print "No Match"
end

Demo

The pattern I wrote should actually work to match any number in brackets, anywhere in the string.

Upvotes: 2

ggorlen
ggorlen

Reputation: 56865

Extract the digits with a regex, convert the capture group to integer and ask Ruby if they're between your bounds:

s = "[499] One too three"
lo = 400
hi = 499

puts s =~ /(\d{3})/ && $1.to_i.between?(lo, hi)

Upvotes: 1

Related Questions