How to allow only special characters as input? - ruby

How can I check if an entered input only has special characters? I tried the following, but its not working:
/^[\p{L}\s\p{N}._#?¿!¡€-]+$/

"!##$%^&()!#" !~ /\w/ # => true
"!a##$%^&()!#" !~ /\w/ # => false

What about this?:
/^[^A-Za-z0-9]+$/
The pattern matches from the beginning to the end of the string and allows one or more characters which are not a letter or a number.

This will match anything that contains only non-word characters (anything but alpha-numeric)
/^[\W]+$/
//edit it also doesn't match _

To check whether an input contains only digits and letters from any alphabet, one might use \p{Alnum} matcher
▶ '¡Hello!' !~ /^\p{Alnum}+$/
#=> false
▶ 'Hello' !~ /^\p{Alnum}+$/
#=> true
▶ '¿Привет?' !~ /^\p{Alnum}+$/
#=> false
▶ 'Привет' !~ /^\p{Alnum}+$/
#=> true
That said, to check for non-alphanumerics:
▶ not '!##$%^&()!#' !~ /^[^\p{Alnum}]+$/
#=> true
▶ not 'a!##$%^&()!#' !~ /^[^\p{Alnum}]+$/
#=> false

Related

Regex for namespaced Ruby class / module names

What is a good regex to match any namespaced Ruby Class or Module name?
More generally, how do I match sequences of words separated by double colons?
Word1::Word2
Word1::Word2::Word3
Word1::Word2::Word3::Word4
etc.
This is the closest thing I got, but it only works for up to two consecutive words:
string.scan /[a-zA-Z0-9]+(?:\:\:[a-zA-Z0-9]+)/
Your approach is fine you should only quantify capturing group or a shorter:
\b\w+(?:::\w+)+\b
Live demo
R = /
\A # match beginning of string
(?: # begin a non-capture group
(?:::)? # optionally match two colons
\p{Lu} # match an uppercase letter
\w* # match zero or more word characters
)+ # close non-capture group and execute group one or more times
\z # match end of string
/x # free-spacing regex definition mode
'AB::CD::EF'.match?(R) #=> true
'A'.match?(R) #=> true
'::A::C_d::E3F_'.match?(R) #=> true
'AB::cD::EF'.match?(R) #=> false
'AB:::CD::EF&'.match?(R) #=> false
Alternatively, we could write the following.
def valid_mod_name?(str)
i = str[0,2]=='::' ? 2 : 0
str[i..-1].split('::').all? { |s| s.match?(/\A\p{Lu}\w*\z/) }
end
valid_mod_name? 'AB::CD::EF' #=> true
valid_mod_name? 'A' #=> true
valid_mod_name? '::A::C_d::E3F_' #=> true
valid_mod_name? 'AB::cD::EF' #=> false
valid_mod_name? 'AB:::CD::EF&' #=> false

Regex doesn't work on the first time

I have a string e.g. 02112016. I want to make a datetime from this string.
I have tried:
s = "02112016"
s.sub(/(\d{2})(\d{2})(\d{4})/, "#{$1}-#{$2}-#{$3}")
But there is a problem. It returns "--".
If I try this s.sub(/(\d{2})(\d{2})(\d{4})/, "#{$1}-#{$2}-#{$3}") again, it works: "02-11-2016". Now I can use to_datetime method.
But why doesn't the s.sub(/(\d{2})(\d{2})(\d{4})/, "#{$1}-#{$2}-#{$3}") work on the first time?
It's really a simple change here. $1 and friends are only assigned after the match succeeds, not during the match itself. If you want to use immediate values, do this:
s = "02112016"
s.sub(/(\d{2})(\d{2})(\d{4})/, '\1-\2-\3')
# => "02-11-2016"
Here \1 corresponds to what will be assigned to $1. This is especially important if you're using gsub since $1 tends to be the last match only while \1 is evaluated for each match individually.
I prefer the following.
r = /
\d{2} # match two digits
(?=\d{4}) # match four digits in a positive lookahead
/x # free-spacing regex definition mode
which is the same as
r = /\d{2}(?=\d{4})/
to be used with String#gsub:
s.gsub(r) { |s| "#{s}-" }
Try it:
"02112016".gsub(r) { |s| "#{s}-" }
#=> "02-11-2016"
What is happening is the first time you ran it, $1, $2, and $3 are empty
You are essentially subbing the numbers for empty strings.
So if we do
s = "02112016"
p $1 #=> nil
p $2 #=> nil
p $3 #=> nil
s.sub(/(\d{2})(\d{2})(\d{4})/, "#{$1}-#{$2}-#{$3}") #=> "--"
p $1 #=> "02"
p $2 #=> "11"
p $3 #=> "2016"
s.sub(/(\d{2})(\d{2})(\d{4})/, "#{$1}-#{$2}-#{$3}") #=> "02-11-2016"
That is why it works the second time.
Since the string is always the same length, you can use the [] method to break it up.
s = "#{s[0..1]}-#{s[2..3]}-#{s[4..-1]}"
This will return the desired result
"02-11-2016"

How to count the number of characters between two characters in a string

This is the question's prompt:
Write a method that takes a string and returns true if the letter
"z" appears within three letters after an "a". You may assume
that the string contains only lowercase letters.
I'm trying to use the ternary operator, and want to include the match or count methods. Any idea on how I can find the number of characters between "a" and "z" or the simplest way to solve this?
def nearby_az(string)
string.count <= 3 ? true : false
end
Regex would be a good way to solve this problem.
You can use online regex testers to experiment with different regexes, inputs and outputs.
The first solution that comes to my mind is to come up with a pattern for each possible correct input:
az
a[a-z]z
a[a-z][a-z]z
Which means:
Match the string "az"
Match a string with "a" and then a character from "a" to "z" and then a "z" character
Match a string with an "a" and then 2 characters from "a" to "z" and then a "z"
and then combine them with the 'or' operator (|)
az|a[a-z]z|a[a-z][a-z]z
Which means match on all three of those conditions.
A link to this example is here.
Doing it this way is a bit verbose so it can be improved by expressing this in a more compact way:
a[a-z]{0,2}z
This means:
Match an "a" then match a character from "a" to "z" 0, 1 or 2 times and then match a "z"
A link to this example is here
You use the method on ruby strings called match which takes in a regex object and then check the boolean return value.
Edit:
The ruby code would look something like this:
def nearby_az(string)
return string.match(/a[a-z]{0,2}z/) != nil
end
string.match() returns an object that you can query to get information about the match. If there is no match, string.match() will return nil.
!!("fjeioaeiz" =~ /a.{,2}z/) #=> true
!!("fjeioaz" =~ /a.{,2}z/) #=> true
!!("fjeioasbdz" =~ /a.{,2}z/) #=> false
Look, Ma! No regex!
def a_upto_4_z(str)
str.each_char.with_index.any? { |c,i| c == ?a && str[i+1,3].include?(?z) }
end
a_upto_4_z "rvaxxzo" #=> true
a_upto_4_z "rvaxxxzo" #=> false
a_upto_4_z "rvaxzo" #=> true
a_upto_4_z "rvazo" #=> true
a_upto_4_z "rvzao" #=> false
Edit: #Stefan makes a good point. Let's do it this way:
def mind_the_gap(str, max_gap=2)
gap = max_gap + 1 # or larger
str.each_char do |c|
case c
when ?z
return true if gap <= max_gap
when ?a
gap = 0
else
gap += 1
end
end
false
end
mind_the_gap "rvaxxzo" #=> true
mind_the_gap "rvaxxxzo" #=> false
mind_the_gap "rvaxzo" #=> true
mind_the_gap "rvazo" #=> true
mind_the_gap "rvzao" #=> false
Note it is not necessary to increment gap when c == ?z and gap > max_gap.

Checking string contents

I have a string like this:
string1 = ",,"
I want to check if there are only commas in my string. Each time string1 changes, it can have any number of commas. How can I check this?
A regex:
Start of string: \A
Comma: , (since it has no special meaning in regexes)
0+ occurrences of the previous matcher: *
End of string: \z
Not \Z! That one's intended to ignore \n at the end, such as the result of readline
The condition is:
/\A,*\z/ =~ your_string
You can find number of ,s in string1 using this:
noc = string1.scan(/,/).size
# => 2
using this, you can verify if the string contains only ,s by doing something like this:
string1=",,"
string1.scan(/,/).size == string1.size
# true
string1=",1,"
string1.scan(/,/).size == string1.size
# false
Use negative range:
",," !~ /[^,]/
# => true
Just out of curiosity:
string1.tr(',', '').empty?
string1.delete(',').empty?
(string1.split('') - [',']).empty?
string1.codepoints.uniq == [44]
def all_commas? str
str.squeeze == ','
end
all_commas? ',' #=> true
all_commas? ',,,' #=> true
all_commas? '3,' #=> false
all_commas? ' ,,,' #=> false
all_commas? ',9,,' #=> false
I'd use this :
string.each_char.all? { |c| c == ',' }
# && !string.empty? if the empty string is not valid
I think it's pretty expressive
try this
string1=",,"
if string1.count(',,') == string1.length
#here is your code
end

Regexp to match repeated substring

I would like to verify a string containing repeated substrings. The substrings have a particular structure. Whole string has a particular structure (substring split by "|"). For instance, the string can be:
1=23.00|6=22.12|12=21.34|112=20.34
1=23.00|6=22.12|12=21.34
1=23.00|12=21.34
1=23.00**
How can I check that all repeated substrings match a regexp? I tried to check it with:
"1=23.00|6=22.12|12=21.34".match(/([1-9][0-9]*[=][0-9\.]+)+/)
But checking gives true even when several substrings do not match the regexp:
"1=23.00|6=ass|=21.34".match(/([1-9][0-9]*[=][0-9\.]+)+/)
# => #<MatchData "1=23.00" 1:"1=23.00">
The question is whether every repeated substring matches a regex. I understand that the substrings are separated by the character | or $/, the latter being the end of a line. We first need to obtain the repeated substrings:
a = str.split(/[#{$/}\|]/)
.map(&:strip)
.group_by {|s| s}
.select {|_,v| v.size > 1 }
.keys
Next we specify whatever regex you wish to use. I am assuming it is this:
REGEX = /[1-9][0-9]*=[1-9]+\.[0-9]+/
but it could be altered if you have other requirements.
As we wish to determine if all repeated substrings match the regex, that is simply:
a.all? {|s| s =~ REGEX}
Here are the calculations:
str =<<_
1=23.00|6=22.12|12=21.34|112=20.34
1=23.00|6=22.12|12=21.34
1=23.00|12=21.34
1=23.00**
_
c = str.split(/[#{$/}\|]/)
#=> ["1=23.00", "6=22.12", "12=21.34", "112=20.34", "1=23.00",
# "6=22.12", "12=21.34", "1=23.00", "12=21.34", "1=23.00**"]
d = c.map(&:strip)
# same as c, possibly not needed or not wanted
e = d.group_by {|s| s}
# => {"1=23.00" =>["1=23.00", "1=23.00", "1=23.00"],
# "6=22.12" =>["6=22.12", "6=22.12"],
# "12=21.34" =>["12=21.34", "12=21.34", "12=21.34"],
# "112=20.34"=>["112=20.34"], "1=23.00**"=>["1=23.00**"]}
f = e.select {|_,v| v.size > 1 }
#=> {"1=23.00"=>["1=23.00", "1=23.00" , "1=23.00"],
# "6=22.12"=>["6=22.12", "6=22.12"],
# "12=21.34"=>["12=21.34", "12=21.34", "12=21.34"]}
a = f.keys
#=> ["1=23.00", "6=22.12", "12=21.34"]
a.all? {|s| s =~ REGEX}
#=> true
This will return true if there are any duplicates, false if there are not:
s = "1=23.00|6=22.12|12=21.34|112=20.34|3=23.00"
arr = s.split(/\|/).map { |s| s.gsub(/\d=/, "") }
arr != arr.uniq # => true
If you want to resolve it through regexp (not ruby), you should match whole string, not substrings. Well, I added [|] symbol and line ending to your regexp and it should works like you want.
([1-9][0-9]*[=][0-9\.]+[|]*)+$
Try it out.

Resources