2

我正在使用 Ruby on Rails 3.2.9,我想验证名称的第一个字符 (a String)不是数字 ( Integer)。我正在尝试使用以下代码:

class User < ActiveRecord::Base
  validates_each :name do |record, attr, value|
    record.errors.add(attr, 'cannot begin with a number') if ... # the first char is a number
  end
end

我怎样才能做到这一点?

4

4 回答 4

6
record.errors.add(attr, 'cannot begin with a number') if value =~ /^[0-9].*/

这将匹配任何第一个字符是数字的字符串

于 2012-12-15T12:33:24.570 回答
3

查看答案,有一系列不同的测试方法。有些让我想知道他们是否会神奇地更快,所以,像往常一样,我做了一个基准测试:

require 'benchmark'

puts "Ruby = #{ RUBY_VERSION }"
str = 'foobar'

puts 'correct result should be false...'
puts !!( str =~ /^\d/                           )
puts !!( str =~ /\A\d/                          )
puts !!( str =~ /^[0-9].*/                      )
puts !!( str.split('').first.to_i.is_a?(Fixnum) )
puts !!( (48..57).include?(str[0])              )

puts !!( ('0'..'9') === str[0]                  )
puts !!( str[/^\d/]                             )
puts !!( str[/\A\d/]                            )
puts !!( str[/\A[0-9]/]                         )
puts !!( str =~ /\A[0-9]/                       )

puts

n = 1_000_000

puts "n = 1_000_000"
puts "str = 'foobar'"
Benchmark::bm(17) do |b|
  b.report('^\d regex')         { n.times { str =~ /^\d/                           } }
  b.report('\A\d regex')        { n.times { str =~ /\A\d/                          } }
  b.report('^[0-9].* regex')    { n.times { str =~ /^[0-9].*/                      } }
  b.report('start_with?')       { n.times { str.start_with?(*('0'..'9'))           } }
  b.report("split('')")         { n.times { str.split('').first.to_i.is_a?(Fixnum) } }
  b.report("(48..57).include?") { n.times { (48..57).include?(str[0])              } }

  b.report('range')           { n.times { ('0'..'9') === str[0] } }
  b.report('str[/^\d/]')      { n.times { str[/^\d/]            } }
  b.report('str[/\A\d/]')     { n.times { str[/\A\d/]           } }
  b.report('str[\A[0-9]')     { n.times { str[/\A[0-9]/]        } }
  b.report('\A[0-9] regex')   { n.times { str =~ /\A[0-9]/      } }
end

puts

str = 'foobar' * 1000
puts "str = 'foobar' * 1000"
Benchmark::bm(17) do |b|
  b.report('^\d regex')         { n.times { str =~ /^\d/                 } }
  b.report('\A\d regex')        { n.times { str =~ /\A\d/                } }
  b.report('^[0-9].* regex')    { n.times { str =~ /^[0-9].*/            } }
  b.report('start_with?')       { n.times { str.start_with?(*('0'..'9')) } }
  b.report("(48..57).include?") { n.times { (48..57).include?(str[0])    } }

  b.report('range')           { n.times { ('0'..'9') === str[0] } }
  b.report('str[/^\d/]')      { n.times { str[/^\d/]            } }
  b.report('str[/\A\d/]')     { n.times { str[/\A\d/]           } }
  b.report('str[\A[0-9]')     { n.times { str[/\A[0-9]/]        } }
  b.report('\A[0-9] regex')   { n.times { str =~ /\A[0-9]/      } }
end

测试结果:

Ruby = 1.9.3
correct result should be false...
false
false
false
true
false
false
false
false
false
false

基准测试结果:

n = 1_000_000
str = 'foobar'
                        user     system      total        real
^\d regex           0.590000   0.000000   0.590000 (  0.593534)
\A\d regex          0.560000   0.000000   0.560000 (  0.556304)
^[0-9].* regex      0.580000   0.000000   0.580000 (  0.577662)
start_with?         4.020000   0.000000   4.020000 (  4.025604)
split('')           6.850000   0.000000   6.850000 (  6.872157)
(48..57).include?  17.260000   0.780000  18.040000 ( 18.038887)
range               1.260000   0.000000   1.260000 (  1.258191)
str[/^\d/]          0.680000   0.000000   0.680000 (  0.680291)
str[/\A\d/]         0.660000   0.000000   0.660000 (  0.663305)
str[\A[0-9]         0.670000   0.000000   0.670000 (  0.670242)
\A[0-9] regex       0.570000   0.000000   0.570000 (  0.574152)

为了测试是否比长字符串\A更快^并查看长字符串会产生什么影响,我增加了字符串大小。 "split('')"被拉出,因为它在 60 多秒后没有完成:

str = 'foobar' * 1000
                        user     system      total        real
^\d regex          15.010000   0.000000  15.010000 ( 15.020488)
\A\d regex          0.540000   0.010000   0.550000 (  0.539736)
^[0-9].* regex     15.000000   0.000000  15.000000 ( 15.011137)
start_with?         4.010000   0.000000   4.010000 (  4.010340)
(48..57).include?  17.320000   0.770000  18.090000 ( 18.124795)
range               1.250000   0.000000   1.250000 (  1.255724)
str[/^\d/]         15.120000   0.010000  15.130000 ( 15.142242)
str[/\A\d/]         0.650000   0.000000   0.650000 (  0.656198)
str[\A[0-9]         0.650000   0.000000   0.650000 (  0.652306)
\A[0-9] regex       0.550000   0.000000   0.550000 (  0.544415)

我用 1.8.7 重新测试:

Ruby = 1.8.7
correct result should be false...
false
false
false
true
false
false
false
false
false
false

n = 1_000_000
str = 'foobar'
                       user     system      total        real
^\d regex          0.570000   0.000000   0.570000 (  0.565397)
\A\d regex         0.550000   0.000000   0.550000 (  0.552270)
^[0-9].* regex     0.570000   0.000000   0.570000 (  0.574705)
start_with?       38.180000   0.070000  38.250000 ( 39.864171)
split('')          9.750000   0.040000   9.790000 ( 11.025962)
(48..57).include?  0.580000   0.000000   0.580000 (  0.917499)
range              2.420000   0.020000   2.440000 (  3.170774)
str[/^\d/]         0.700000   0.000000   0.700000 (  0.760180)
str[/\A\d/]        0.680000   0.000000   0.680000 (  0.762636)
str[\A[0-9]        0.660000   0.010000   0.670000 (  0.795043)
\A[0-9] regex      0.600000   0.000000   0.600000 (  0.684566)

str = 'foobar' * 1000
                       user     system      total        real
^\d regex          7.900000   0.040000   7.940000 ( 10.735175)
\A\d regex         0.600000   0.010000   0.610000 (  0.784001)
^[0-9].* regex     7.850000   0.020000   7.870000 (  8.251673)
(48..57).include?  0.580000   0.000000   0.580000 (  0.683730)
range              2.380000   0.020000   2.400000 (  2.738234)
str[/^\d/]         7.930000   0.010000   7.940000 (  8.227906)
str[/\A\d/]        0.670000   0.000000   0.670000 (  0.682169)
str[\A[0-9]        0.680000   0.000000   0.680000 (  0.697340)
\A[0-9] regex      0.580000   0.000000   0.580000 (  0.645136)

你们自己讨论。

于 2012-12-15T16:04:05.960 回答
1

刚刚发现你可以喷出一个范围:

"1hello".start_with?(*('0'..'9')) #=> true
于 2012-12-15T15:13:54.733 回答
0

您可以通过数组访问获取字符串的第一个字符并比较 ascii 值:

1.8.7 :008 > (48..57).include?("5ssdfsdf"[0])
 => true 
1.8.7 :009 > (48..57).include?("ssdfsdf"[0])
 => false 
1.8.7 :010 > (48..57).include?("0sdfsdf"[0])
 => true 
1.8.7 :011 > (48..57).include?("9sdfsdf"[0])
 => true 
于 2012-12-15T16:06:49.617 回答