Regex for a number 1-31 Ruby - ruby

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?

The following regex satisfies your condition:
^([1-9]|[12][0-9]|3[01])$
Demo here

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]

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

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)$/

Related

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?
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:
Check that you are at the beginning of a line, or have a non-digit character followed by
the digit '4' followed by
Any 2 digits followed by
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
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"]
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.
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)

Simple regex - ignoring certain characters

I'm trying to use the match method with an argument of a regex to select a valid phone number, by definition, any string with nine digits.
For example:
9347584987 is valid,
(456)322-3456 is valid,
(324)5688890 is valid.
But
(340)HelloWorld is NOT valid and
456748 is NOT valid.
So far, I'm able to use \d{9} to select the example string of 9 digit characters in a row, but I'm not sure how to specifically ignore any character, such as '-' or '(' or ')' in the middle of the sequence.
What kind of Regex could I use here?
Given:
nums=['9347584987','(456)322-3456','(324)5688890','(340)HelloWorld', '456748 is NOT valid']
You can split on a NON digit and rejoin to remove non digits:
> nums.map {|s| s.split(/\D/).join}
["9347584987", "4563223456", "3245688890", "340", "456748"]
Then filter on the length:
> nums.map {|s| s.split(/\D/).join}.select {|s| s.length==10}
["9347584987", "4563223456", "3245688890"]
Or, you can grab a group of numbers that look 'phony numbery' by using a regex to grab digits and common delimiters:
> nums.map {|s| s[/[\d\-()]+/]}
["9347584987", "(456)322-3456", "(324)5688890", "(340)", "456748"]
And then process that list as above.
That would delineate:
> '123 is NOT a valid area code for 456-7890'[/[\d\-()]+/]
=> "123" # no match
vs
> '123 is NOT a valid area code for 456-7890'.split(/\D/).join
=> "1234567890" # match
I suggest using one regular expression for each valid pattern rather than constructing a single regex. It would be easier to test and debug, and easier to maintain the code. If, for example, "123-456-7890" or 123-456-7890 x231" were in future deemed valid numbers, one need only add a single, simple regex for each to the array VALID_PATTERS below.
VALID_PATTERS = [/\A\d{10}\z/, /\A\(\d{3}\)\d{3}-\d{4}\z/, /\A\(\d{3}\)\d{7}\z/]
def valid?(str)
VALID_PATTERS.any? { |r| str.match?(r) }
end
ph_nbrs = %w| 9347584987 (456)322-3456 (324)5688890 (340)HelloWorld 456748 |
ph_nbrs.each { |s| puts "#{s.ljust(15)} \#=> #{valid?(s)}" }
9347584987 #=> true
(456)322-3456 #=> true
(324)5688890 #=> true
(340)HelloWorld #=> false
456748 #=> false
String#match? made its debut in Ruby v2.4. There are many alternatives, including str.match(r) and str =~ r.
"9347584987" =~ /(?:\d.*){9}/ #=> 0
"(456)322-3456" =~ /(?:\d.*){9}/ #=> 1
"(324)5688890" =~ /(?:\d.*){9}/ #=> 1
"(340)HelloWorld" =~ /(?:\d.*){9}/ #=> nil
"456748" =~ /(?:\d.*){9}/ #=> nil
Pattern: (Rubular Demo)
^\(?\d{3}\)?\d{3}-?\d{4}$ # this makes the expected symbols optional
This pattern will ensure that an opening ( at the start of the string is followed by 3 numbers the a closing ).
^(\(\d{3}\)|\d{3})\d{3}-?\d{4}$
On principle, though, I agree with melpomene in advising that you remove all non-digital characters, test for 9 character length, then store/handle the phone numbers in a single/reliable/basic format.

Searching for vowels in Ruby

str = "Find the vowels in this string or else I'll date your sister"
I am looking to count the number of vowels in a string and I believe I have achieved this, but I have done it by appending the each letter to an array and taking the length of the array. What's a more common way to do this. Maybe with +=?
str.chars.to_a.each do |i|
if i =~ /[aeiou]/
x.push(i)
end
end
x.length
But here is even better answer =). It turns out that we have a String#count method:
str.downcase.count 'aeiou'
#=> 17
If you want to count the vowels, why not use count:
str.chars.count {|c| c =~ /[aeiou]/i }
Use scan
"Find the vowels in this string or else I'll date your sister".scan(/[aeiou]/i).length
No need to:
str.chars.to_a
In fact, str.chars already is a Array
> String.new.chars.class
=> Array
Refactoring a little
str.chars.each{|i| i =~ /[aeiou]/ ? x : nil}
x.length
But maybe an alternative for the best solution could be:
a.chars.map{|x| x if x.match(/[aeiouAEIOU]/)}.join.size
You should check the map block because you could perform something useful inside, as an alternative just for the count block.
without doubt best solution for count vowel inside string using block:
str.chars.count {|c| c =~ /[aeiou]/i }
There are shorter incarnations.
$ irb
>> "Find the vowels in this string or else I'll date your sister".gsub(/[^aeiou]/i, '').length
=> 17
Here's a way that use String#tr:
str = "Find the vowels in this string or else I'll date your sister"
str.size - str.tr('aeiouAEIOU','').size #=> 17
or
str.size - str.downcase.tr('aeiou','').size #=> 17

Checking string with minimum 8 digits using regex

I have regex as follows:
/^(\d|-|\(|\)|\+|\s){12,}$/
This will allow digits, (, ), space. But I want to ensure string contains atleast 8 digits.
Some allowed strings are as follows:
(1323 ++24)233
24243434 43
++++43435++4554345 434
It should not allow strings like:
((((((1213)))
++++232+++
Use Look ahead within your regex at the start..
/^(?=(.*\d){8,})[\d\(\)\s+-]{8,}$/
---------------
|
|->this would check for 8 or more digits
(?=(.*\d){8,}) is zero width look ahead that checks for 0 to many character (i.e .*) followed by a digit (i.e \d) 8 to many times (i.e.{8,0})
(?=) is called zero width because it doesnt consume the characters..it just checks
To restict it to 14 digits you can do
/^(?=([^\d]*\d){8,14}[^\d]*$)[\d\(\)\s+-]{8,}$/
try it here
Here's a non regular expression solution
numbers = ["(1323 ++24)233", "24243434 43" , "++++43435++4554345 434", "123 456_7"]
numbers.each do |number|
count = 0
number.each_char do |char|
count += 1 if char.to_i.to_s == char
break if count > 7
end
puts "#{count > 7}"
end
No need to mention ^, $, or the "or more" part of {8,}, or {12,}, which is unclear where it comes from.
The following makes the intention transparent.
r = /
(?=(?:.*\d){8}) # First condition: Eight digits
(?!.*[^-\d()+\s]) # Second condition: Characters other than `[-\d()+\s]` should not be included.
/x
resulting in:
"(1323 ++24)233" =~ r #=> 0
"24243434 43" =~ r #=> 0
"++++43435++4554345 434" =~ r #=> 0
"((((((1213)))" =~ r #=> nil
"++++232+++" =~ r #=> nil

regular expression in Ruby: check the total number of digits

I need a one line gsub to replace all the non-digits in a string but only if the non-digits are not more than three and if the total length of the digits is 10
I have this which fits the first condition
p "0177/385490".gsub(/((\d+)\D?(\d+)\D?(\d+)\D?+(\d+))/,'\2\3\4\5')
#=>"0177385490"
but when i try this the {10} check doesn't work
p "0177/385490".gsub(/((\d+)\D?(\d+)\D?(\d+)\D?+(\d+)){10}/,'\2\3\4\5')
#=>"0177/385490"
how to do this please ?
EDIT
i managed to to it like this, but how to do this in a oneline gsub ?
strings = [
"0473/385 490",
"0473/385490",
"0473 38 54 90",
"0473/385 4901" #this one is't captured
]
strings.each do |s|
if /((\d+)\D?(\d+)\D?(\d+)\D?+(\d+))/ =~ s
if "#{$2}#{$3}#{$4}#{$5}".length == 10
puts "#{$2}#{$3}#{$4}#{$5}"
end
end
end
EDIT: to show why it really needs to be a onle line gsub here my routine, there will be more replacements added
def cleanup text
replacements = [
{:pattern => /(04\d{2}) (\d{2}) (\d{2}) (\d{2})/, :replace_with => '\1\2\3\4'},
{:pattern => /(0\d)(\/| |-)(\d{3}) (\d{2}) (\d{2})/, :replace_with => '\1\3\4\5'},
{:pattern => /(\d{6} )(\d{3})-(\d{2})/, :replace_with => '\1\2 \3'},
{:pattern => /(\d{2,4})\D?(\d{2,3})\D?(\d{2,3})/, :replace_with => '\1\2\3'}
].each{|replacement|text.gsub!(replacement[:pattern], replacement[:replace_with])}
text
end
I think a one-line gsub wouldn't be overly readable. Here's my approach:
chars, non_chars = s.each_char.partition { |c| c =~ /\d/ }
puts chars.join if chars.size == 10 && non_chars.size <= 3
Clean and easy to read, without any magic variables. Plus it clearly shows the rules you have imposed on the string.
Here's a one-liner with gsub, mostly to illustrate why Michael Kohl's approach is better:
(digits = s.gsub(/\D/, '')).length == 10 && s.length < 14 ? digits : s
You may use something like this:
puts s.gsub(/\D/, '') if (/\A(\d\D?){10}\z/ =~ s) && (/\A(\d+\D){0,3}\d*\z/ =~ s)
You might also want to know about the scan method.
strings.each do |s|
numbers = s.scan(/\d/).join
non_numbers = s.scan(/\D/)
puts numbers if numbers.length == 10 && non_numbers.length < 4
end
But I like the solution by #MichaelKohl better.
And then a silly example:
strings.select{|s| s.scan(/\D/).length < 4}.map{|s| s.scan(/\d/).join}.select{|s| s.length==10}
Thanks everyone but i can't use the answers because i can't insert them in my routine (edited my answer to make that more clear). Found a sollution myself. I give everyone an upvote who had a one line solution as requested, now i still need to find a way to insert my block as a replacementpattern in the cleanup routine
p "0177/3854901".gsub(/(\d+)\D?(\d+)\D?(\d+)\D?+(\d+)/){ |m| "#{$1}#{$2}#{$3}#{$4}".length==10 ? "#{$1}#{$2}#{$3}#{$4}":m}
#=> "0177/3854901" isn't replaced because it has 11 digits
p "0177/385490".gsub(/(\d+)\D?(\d+)\D?(\d+)\D?+(\d+)/){ |m| "#{$1}#{$2}#{$3}#{$4}".length==10 ? "#{$1}#{$2}#{$3}#{$4}":m}
#=> "0177385490"

Resources