如何使用正则表达式检查用户名格式

时间:2017-02-07 02:40:11

标签: ruby-on-rails ruby regex

我需要使用正则表达式检查用户名格式。 我的用户名标准是:

  • 任何地方都必须包含1个或多个字母。
  • 任何地方都可以包含任意数量的数字。
  • 最多可包含2个或_,任何地方。
我正在使用

^[0-9\-_]*[a-z|A-Z]+[0-9\-_]*$,但这会拒绝123hi123hihi123hi等用户名。我需要一些不依赖于字符串位置的东西。

我正在使用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

4 个答案:

答案 0 :(得分:1)

您可以使用以下两种方法。

构建单个正则表达式

因为正则表达式涉及字符串中字符的排序,所以必须为以下每个组合构造一个正则表达式,然后&#34;或&#34;那些正则表达式是一个正则表达式。

  • 一个字母,零连字符,零下划线
  • 一个字母,零连字符,一个下划线
  • 一个字母,零连字符,两个下划线
  • 一个字母,一个连字符,零下划线
  • 一个字母,一个连字符,一个下划线
  • 一个字母,一个连字符,两个下划线
  • 一个字母,两个连字符,零下划线
  • 一个字母,两个连字符,一个下划线
  • 一个字母,两个连字符,两个下划线

数字和附加字母可以出现在用户名中的任何位置。

让我们调用正则表达式t0t1,...,t8。所需的单个整体正则表达式为:

/#{t0}|#{t1}|...|#{t8}/

让我们考虑构造t4(一个字母,一个连字符,一个下划线)。 这种组合可能有六种可能的订单。

  • 一封信,一个连字符,一个下划线
  • 一封信,一个下划线,一个连字符
  • 连字符,字母,下划线
  • 连字符,下划线,字母
  • 下划线,字母,连字符
  • 下划线,连字符,字母

我们需要为这六个订单(r0r1,...,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中的每一个都是类似的。

然后,我们需要为除第五个组合之外的八个组合中的每个组合计算tit0(一个字母,零连字符,零下划线)很简单:

t0 = /\A[a-z0-9]*[a-z][a-z0-9]*\z/i

相比之下,t8(一个字母,两个连字符,两个下划线)将比t4(上面考虑过)更长的正则表达式,因为正则表达式必须是手工制作的每个5!/(2!*2!) #=> 30排序(r0r1,...,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

另外,为什么不对特殊字符进行计数,例如在上面的条件中?