1

I am trying to match any number 1-31 (inclusively).

This is the closest I have:

([1-9]|[12]\d|3[01])

But numbers like 324 are accepted.

Any chance there's a regex out there that can capture just 1-31?

vgoff
  • 10,980
  • 3
  • 38
  • 56
user3007294
  • 931
  • 1
  • 14
  • 33
  • Your regex is really close to being correct, but the problem is that it will match the '3' in '324' and your test will pass. If you add in the start of string and end of string markers, I think you'll have what you want: `\A([1-9]|[12]\d|3[01])\z` – JKillian Oct 13 '14 at 00:04
  • You could just put the word boundary token around your regex if it is a part of a string: **\b([1-9]|[12]\d|3[01])\b** Or use jKillian suggestion if it is the entire string. – Ron Rosenfeld Oct 13 '14 at 00:22
  • awesome, thanks all. great suggestions! seems there are multiple solutions to work from. – user3007294 Oct 13 '14 at 00:37
  • 1
    Regex aren't good tools for testing whether values are with a certain range. They result in unwieldy patterns. Instead use them to extract the value then compare them to a range like `1..31`. – the Tin Man Oct 13 '14 at 05:06
  • Am I missing something? All the answers using only a regex have anchors to ensure that an entire line is a representation of the number, as opposed to extracting a number from an arbitrary string. You said nothing about that in your question. Please clarify. – Cary Swoveland Oct 13 '14 at 06:44

4 Answers4

7

The following regex satisfies your condition:

^([1-9]|[12][0-9]|3[01])$

Demo here

arco444
  • 22,002
  • 12
  • 63
  • 67
  • zero? The problem with this is that it's easy to overlook something. – pguardiario Oct 13 '14 at 01:35
  • As @pguardiaario noted, `"0"[/^([0-9]|[12][0-9]|3[01])$/] => "0"`, so please change that first zero to a one. Also, why the anchors? What about, "It is now 25 days and counting."? – Cary Swoveland Oct 13 '14 at 06:36
  • @CarySwoveland Easy to take the anchors out if not required. OP didn't mention _anything_ about it being used in a sentence so I don't see why that's in any way incorrect. Have corrected the zero. – arco444 Oct 13 '14 at 09:38
6

Use a Numeric Comparison Instead

Depending on what you are really trying to do, or to communicate with your code, it may make more sense to simply extract all integers and reject those outside your desired range. For example:

str = '0 1 20 31 324'
str.scan(/\d+/).map(&:to_i).reject { |i| i < 1 or i > 31 }
#=> [1, 20, 31]
Todd A. Jacobs
  • 81,402
  • 15
  • 141
  • 199
  • thanks for the suggestion CodeGnome! i didn't even think to do this. in the future, i will definitely try something like this out. however, for this specific case, i believe a regular expression would work best. – user3007294 Oct 13 '14 at 00:38
  • Good answer. Perhaps throw some non-numeric values in your `str` just to show `scan` works for that (and `str.split` doesn't). A stylistic variant: `.select { |i| (1..31).cover?(i) }`. – Cary Swoveland Oct 13 '14 at 06:21
2

Try with this one:/^([0-9]|1[0-9]|2[0-9]|3[01])$/

Here an example:

str = STDIN.gets.chomp

if str =~ /^([0-9]|1[0-9]|2[0-9]|3[01])$/
    puts "Match!"
else
    puts "No match!"
end
rendon
  • 2,323
  • 1
  • 19
  • 25
0

Here's one:

/^(#{(1..31).to_a * '|'})$/
#=> /^(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)$/
pguardiario
  • 53,827
  • 19
  • 119
  • 159