我需要使用正则表达式检查用户名格式。 我的用户名标准是:
^[0-9\-_]*[a-z|A-Z]+[0-9\-_]*$
,但这会拒绝123hi123hi
或hi123hi
等用户名。我需要一些不依赖于字符串位置的东西。
我正在使用Ruby on Rails来匹配字符串。
Rails非常低效的Ruby函数版本是:
validate :check_username
def check_username
if self.username.count("-") > 2
errors.add(:username, "cannot contain more than 2 dashes")
elsif self.username.count("_") > 2
errors.add(:username, "cannot contain more than 2 underscores")
elsif self.username.count("a-zA-Z") < 1
errors.add(:username, "must contain a letter")
elsif (self.username =~ /^[0-9a-zA-Z\-_]+$/) !=0
errors.add(:username, "cannot contain special characters")
end
end
答案 0 :(得分:1)
您可以使用以下两种方法。
构建单个正则表达式
因为正则表达式涉及字符串中字符的排序,所以必须为以下每个组合构造一个正则表达式,然后&#34;或&#34;那些正则表达式是一个正则表达式。
数字和附加字母可以出现在用户名中的任何位置。
让我们调用正则表达式t0
,t1
,...,t8
。所需的单个整体正则表达式为:
/#{t0}|#{t1}|...|#{t8}/
让我们考虑构造t4
(一个字母,一个连字符,一个下划线)。
这种组合可能有六种可能的订单。
我们需要为这六个订单(r0
,r1
,...,r5
)中的每一个构建一个正则表达式,然后是&#34;或者#34;他们获得t4
:
t4 = /#{r0}|#{r1}|#{r2}|#{r3}|#{r4}|#{r5}/
现在让我们考虑构建一个正则表达式r0
,它将实现这些排序中的第一个(字母,连字符,下划线):
r0 = /\A[a-z0-9]*[a-z][a-z0-9]*-[a-z0-9]*_[a-z0-9]*\z/i
"3ab4-3cd_e5".match?(r0) #=> true
"3ab4-3cde5".match?(r0) #=> false (no underscore)
"34-3cd_e5".match?(r0) #=> false (no letter before the hyphen)
"3ab4_3cd-e5".match?(r0) #=> false (underscore precedes hyphen)
构建其他五个ri
中的每一个都是类似的。
然后,我们需要为除第五个组合之外的八个组合中的每个组合计算ti
。 t0
(一个字母,零连字符,零下划线)很简单:
t0 = /\A[a-z0-9]*[a-z][a-z0-9]*\z/i
相比之下,t8
(一个字母,两个连字符,两个下划线)将比t4
(上面考虑过)更长的正则表达式,因为正则表达式必须是手工制作的每个5!/(2!*2!) #=> 30
排序(r0
,r1
,...,r29
)。
现在显而易见的是,使用单个正则表达式并不是验证用户名的正确工具。
不构建单个正则表达式
def username_valid?(username)
cnt = username.each_char.with_object(Hash.new(0)) do |c,cnt|
case c
when /\d/
when /[[:alpha:]]/
cnt[:letter] += 1
when '-'
cnt[:hyphen] += 1
when '_'
cnt[:underscore] += 1
else
return false
end
end
cnt.fetch(:letter, 0) > 0 && cnt.fetch(:hyphen, 0) <= 2 &&
cnt.fetch(:underscore, 0) <= 2
end
username_valid? "Bob" #=> true
username_valid? "Bob1_23_-" #=> true
username_valid? "z" #=> true
username_valid? "123--_" #=> false (no letters)
username_valid? "Melba1-23--_" #=> false (3 hyphens)
username_valid? "Bob1_23_-$" #=> false ($ not permitted)
带参数(默认值)为零的 Hash#new通常称为计算哈希。如果h
是没有键k
的哈希,则h[k]
会返回默认值。因此评估如下:
h[k] += 1
#=> h[k] = h[k] + 1
#=> h[k] = 0 + 1
只要确定正则表达式不正确,就可以编写该方法以返回false
。
def username_valid?(username)
cnt = username.each_char.with_object(Hash.new(0)) do |c,cnt|
case c
when /\d/
when /[[:alpha:]]/
cnt[:letter] += 1
when '-'
return false if cnt[:hyphen] == 2
cnt[:hyphen] += 1
when '_'
return false if cnt[:underscore] == 2
cnt[:underscore] += 1
else
return false
end
end
cnt.fetch(:letter, 0) > 0
end
答案 1 :(得分:0)
这对于正则表达式来说是一个很糟糕的用途,因为您的数据不够结构化。相反,一系列简单的测试会告诉您需要知道的内容:
def valid?(str)
str[/[a-z]/i] && str.tr('^-_', '').size <= 2
end
%w(123hi123hi hi123hi).each do |username|
username # => "123hi123hi", "hi123hi"
valid?(username) # => true, true
end
由于使用正则表达式而导致速度降低
/[a-z]/i
所以改为
def valid?(str)
str.downcase.tr('^a-z', '').size >= 0 && str.tr('^-_', '').size <= 2
end
可以使用。基于测试,正则表达式的使用速度降低约45%。
打破它:
str[/[a-z]/i]
测试至少一个字符。因为可以有多个就足够了。 str.downcase.tr('^a-z', '').size
将字符转换为小写,然后删除所有非字母字符,结果只剩下字母,然后计算有多少字符:
'123hi123hi'.downcase # => "123hi123hi"
.tr('^a-z', '') # => "hihi"
.size # => 4
'hi123hi'.downcase # => "hi123hi"
.tr('^a-z', '') # => "hihi"
.size # => 4
'hi-123_hi'.downcase # => "hi-123_hi"
.tr('^a-z', '') # => "hihi"
.size # => 4
规则
任何地方都可以包含任意数量的数字
不值得测试,所以我忽略了它。
答案 2 :(得分:-1)
这是你的正则表达式的改进版本
^[\w-]*[A-Za-z]+[\w-]*$
但这将无法计算多少 - 或_那里,所以你需要另一个正则表达式来过滤或手动编写代码。 这是只检查两个或更少[-_]的正则表达式,无视其位置:
^[A-Za-z\d]*[-_]{0,1}[A-Za-z\d]*[-_]{0,1}[A-Za-z\d]*$
答案 3 :(得分:-1)
如果您只允许使用字母,数字,短划线和下划线,
其他一切都被认为是一个特殊的角色,
我认为只有你所拥有的模式需要否定。
而不是(self.username =~ /^[0-9a-zA-Z\-_]+$/) !=0
尝试(self.username =~ /^[^0-9a-zA-Z\-_]+$/) !=0
或(self.username =~ /^[\W-]+$/) > 0
。
另外,为什么不对特殊字符进行计数,例如在上面的条件中?