Is There a Way of Changing Regular Expressions to a Range of Numbers in Ruby on Rails

How to match all occurrences of a regular expression in Ruby

Using scan should do the trick:

string.scan(/regex/)

How to specify a range in Ruby

Regex really is the right way to do this. It's specifically for testing patterns in strings. This is how you'd test "do all characters in this string fall in the range of characters 0-9?":

pin.match(/\A[0-9]+\z/)

This regex says "Does this string start and end with at least one of the characters 0-9, with nothing else in between?" - the \A and \z are start-of-string and end-of-string matchers, and the [0-9]+ matches any one or more of any character in that range.

You could even do your entire check in one line of regex:

pin.match(/\A([0-9]{4}|[0-9]{6})\z/)

Which says "Does this string consist of the characters 0-9 repeated exactly 4 times, or the characters 0-9, repeated exactly 6 times?"

Ruby's String#count method does something similar to this, though it just counts the number of occurrences of the characters passed, and it uses something similar to regex ranges to allow you to specify character ranges.

The sequence c1-c2 means all characters between c1 and c2.

Thus, it expands the parameter "0-9" into the list of characters "0123456789", and then it tests how many of the characters in the string match that list of characters.

This will work to verify that a certain number of numbers exist in the string, and the length checks let you implicitly test that no other characters exist in the string. However, regexes let you assert that directly, by ensuring that the whole string matches a given pattern, including length constraints.

rails - using RE to extract locale from HTTP_ACCEPT_LANGUAGE

@npinti and @Victor's answers are good from the perspective of "regex". However, they are not useful enough when the topic is "using RE to extract locale from HTTP_ACCEPT_LANGUAGE in rails". To detect both 2 chars(eg, "en") and 5 chars(eg, "en-US") format properly in rails:

# accept_language should be something like 
# "en-US,en;q=0.8,zh-TW;q=0.6,zh;q=0.4" (from chrome)
# however, it may be nil if the client doesn't set accept language in header.
accept_language = request.env['HTTP_ACCEPT_LANGUAGE'] || ""
# use "match" instead of "scan"!!
match_data = accept_language.match(/^[a-z]{2}(-[A-Z]{2})?/)
I18n.locale = match_data ? match_data[0] : I18n.default_locale

Regex - Cost of an Item

Of course, the correct way would be to take the provided string, convert it to a number (catching errors if it's not a parseable number) and then compare that with the valid values.

If it has to be a regex, it's of course possible but ugly:

^(?:[1-9][0-9]?(?:\.[0-9]{1,2})?|0?.0[1-9]|0?.[1-9][0-9]?)$

Explanation:

^                   # start of string
(?: # either match
[1-9][0-9]? # a number between 1 and 99
(?:\.[0-9]{1,2})? # optionally followed by a decimal point and up to two digits
| # or
0?.0[1-9] # a number between 0.01 and 0.09 (optionally without leading 0)
| # or
0?.[1-9][0-9]? # a number between 0.1 and 0.99
) # end of alternation
$ # end of string

Of course, in most regex dialects, you can use \d in place of [0-9] without a change in meaning, but I think in this case sticking to the longer version helps readability.

In Ruby, assuming your input string never contains a newline:

if subject =~ /^(?:[1-9][0-9]?(?:\.[0-9]{1,2})?|0?.0[1-9]|0?.[1-9][0-9]?)$/
# Successful match
else
# Match attempt failed
end

Since you care about the number of significant digits, another solution would be to first check if the input looks like a number, and if it passes that test, convert it to a float and check if it's in range.

^(\d{1,2}|\d{0,2}\.\d{1,2})$

would match any number (integer or decimal up to two digits after the decimal point) between 0 and 99.99. Then you just need to check whether the number is >= 0.01. The advantage of this approach is that you can easily extend the range of digits allowed before/after the decimal point if the requirements for valid numbers change, and then adjust the value check accordingly.

Define a regex, which matches one digit twice and all others once

You can use this kind of pattern:

\A(?=\d{11}\z)(?:(\d)(?!\d*\1\d))*(\d)(?=\d*\2\d)(?:(\d)(?!\d*\3\d))+\d\z

online demo

pattern details:

the idea is to describe string as a duplicate digit surrounded by non duplicate digits.

Finding a duplicate digit is easy with a capture group, a lookahead assertion and a backreference:
(\d)(?=\d*\1)

You can use the same pattern to ensure that a digit has no duplicate, but this time with a negative lookahead: (\d)(?!\d*\1)

To not take in account the last digit (digit n°11) in the search of duplicates, you only need to add a digit after the backreference. (\d)(?=\d*\1\d) (in this way you ensure there is at least one digit between the backreference and the end of the string.)

Note that in the present context, what is called a duplicate digit is a digit that is not followed immediatly or later with the same digit. (i.e. in 1234567891 the first 1 is a duplicate digit, but the last 1 is no more a duplicate digit because it is not followed by an other 1)

\A                       # begining of the string
(?=\d{11}\z) # check the string length (if not needed, remove it)
(?:(\d)(?!\d*\1\d))* # zero or more non duplicate digits
(\d)(?=\d*\2\d) # one duplicate digit
(?:(\d)(?!\d*\3\d))+ # one or more non duplicate digits
\d # the ignored last digit
\z # end of the string

an other way

This time you check the duplicates at the begining of the pattern with lookaheads. One lookahead to ensure there is one duplicate digit, one negative lookahead to ensure there are not two duplicate digits:

\A(?=\d*(\d)(?=\d*\1\d))(?!\d*(\d)(?=\d*\2\d)\d*(\d)(?=\d*\3\d))\d{11}\z

pattern details:

\A
(?= # check if there is one duplicate digit
\d*(\d)(?=\d*\1\d)
)
(?! # check if there are not two duplicate digits
\d*(\d)(?=\d*\2\d) # the first
\d*(\d)(?=\d*\3\d) # the second
)
\d{11}
\z

Note: However it seems that the first way is more efficient.

The code way

You can easily check if your string fit the requirements with array methods:

> mydigs = "12345678913"
=> "12345678913"
> puts (mydigs.split(//).take 10).uniq.size == 9
true
=> nil

How do I match non-letters and non-numbers after a bunch of numbers?

There are a couple of issues with the regex.

1) When you are using a double quoted string literal in a Regexp.new constructor, to declare a literal backslash you need to double it (\p => \\p)

2) [^\p{L}^0-9] is is a wrong construct for any char but a letter and digit because the second ^ is treated as a literal ^ symbol. You need to remove the second ^ at least. You may also use [^[:alnum:]] to match any non-alphanumeric symbol.

3) The pattern above matches whitespaces, too, so you do not need to alternate it with [[:space]]. ([[:space:]]|[^\p{L}^0-9])* -> [^\p{L}0-9]*.

So, you may use your fixed Regexp.new("\\A[^\\p{L}0-9]*\\d+[^\\p{L}0-9]*\\z") regexp, or use

/\A[^[:alnum:]]*\d+[^[:alnum:]]*\z/.match?(token.downcase)

See the Rubular demo where your sample string is not matched with the regex.

Details:

  • \A - start of a string
  • [^[:alnum:]]* - 0+ non-alphanumeric chars
  • \d+ - 1+ digits
  • [^[:alnum:]]* - 0+ non-alphanumeric chars
  • \z - end of string.

Rails SQL regular expression

You did a good job! The thing missing was the REGEXP function which is used for regex in queries:

So in your case use

Drawing.where("drawing_number REGEXP ?", 'A\d{4}')
# the {4} defines that there have to be exactly 4 numbers, change if you need to

In SQL you use the '-colons, which is weird because you normally start regex with /-backslashes



Related Topics



Leave a reply



Submit