Фон

Правильная реализация проверки ввода - один из самых фундаментальных аспектов любого веб-приложения. Хотя мы можем полагаться на некоторые из существующих методов для достижения многих типов проверки ввода, некоторые методы могут быть не такими гибкими, как нам хотелось бы.

Например, метод Object#is_a? позволяет нам проверить, имеет ли вызывающий объект определенный тип:

>> 12.is_a? Integer
=> true
>> 'foo'.is_a? String
=> true
>> [1, 2, 3].is_a? Array
=> false

Этот метод работает хорошо, пока мы не введем объекты, которые выглядят как число, массив и т. Д.:

>> '12'.is_a? Integer
=> false
>> '[1, 2, 3]'.is_a? Array
=> false

Причина, по которой они возвращают false, конечно же, заключается в том, что эти вызывающие объекты на самом деле являются строками, а не целым числом или массивом.

Здесь мы хотим избежать обработки этих входных данных как строк, поскольку мы заинтересованы в проверке того, что находится «внутри» строк.

Разве не было бы неплохо, если бы у нас был метод, который мог бы иметь любые типы аргументов в качестве входных данных, проверять «внутреннее значение» входных данных и проверять, относится ли он к определенному типу, который мы указываем?

Реализация

Описанная ниже реализация ориентирована на проверку числа в Ruby. Точнее, он проверит, принадлежит ли «содержимое» данного ввода произвольного типа к одному из следующих типов (классов): Integer, Float.

def number?(obj)
  obj = obj.to_s unless obj.is_a? String
  /\A[+-]?\d+(\.[\d]+)?\z/.match(obj)
end

Начнем с анализа регулярного выражения:

/\A[+-]?\d+(\.\d+)?\z/

Вот список элементов, содержащихся в регулярном выражении.

/         start of the regex
\A        start of the string to be matched
[+-]?     zero or one of '+' or'-'
\d+       one or more of digit
(\.\d+)?  zero or one of 'one dot and 'one or more of digit'' 
\z        end of the string to be matched
/         end of the regex

(Для получения более подробной информации о синтаксисе регулярных выражений в целом вы можете проверить этот краткий справочник по регулярным выражениям.)

Давайте перейдем к irb, чтобы убедиться, что регулярное выражение действительно работает:

(irb)
REGEX = /\A[+-]?\d+(\.[\d]+)?\z/
REGEX.match '13'
=> #<MatchData "13" 1:nil>
!!REGEX.match '13'
=> true
REGEX.match '3.14'
=> #<MatchData "3.14" 1:".14">
!!REGEX.match '3.14'
=> true
REGEX.match 'not a number'
=> nil
!!REGEX.match 'not a number'
=> false

Метод Regexp # match в приведенном выше примере возвращает объект MatchData, если проверяемая строка представляет собой число. В противном случае он вернет nil. Обозначение с двойным восклицательным знаком (!!) используется здесь для преобразования возвращаемого значения в соответствующее ему логическое значение.

Обратите внимание, что метод сопоставления выдаст ошибку при передаче в качестве аргумента нестрокового объекта:

(irb)
REGEX = /\A[+-]?\d+(\.[\d]+)?\z/
REGEX.match 13
=> TypeError: no implicit conversion of Integer into String ...
REGEX.match 3.14
=> TypeError: no implicit conversion of Float into String ...

Чтобы этого избежать, мы добавим следующий код (*):

obj.to_s unless obj.is_a? String                                 (*)

(«Obj» - это объект, который мы хотим протестировать на соответствие регулярному выражению) Эта строка кода преобразует объект в объект String, если объект не относится к типу String, и гарантирует, что мы можем использовать этот объект String для проверки на соответствие регулярному выражению.

Это завершает объяснение того, как вышеуказанный метод:

def number?(obj)
  obj = obj.to_s unless obj.is_a? String
  /\A[+-]?\d+(\.[\d]+)?\z/.match obj
end

работает.

Вот пример того, как мы можем использовать этот метод (в этом примере код (*) является необязательным, поскольку «число» всегда будет иметь тип String после присвоения значения через gets.chomp, метод, который возвращает строку:

puts "Enter a number:"
number = nil
loop do
  number = gets.chomp
  break if number?(number)
  puts "That is not a number."
end
puts "#{number} is indeed a number."

Запуск этого кода выводит на печать следующее:

Enter a number:
foo
That is not a number.
bar
That is not a number.
‘12.34’
That is not a number.
12.3.4
That is not a number.
12.34
12.34 is indeed a number.

использованная литература

Вам также могут быть полезны следующие ресурсы по этой теме:

«Http://stackoverflow.com/questions/1235863/test-if-a-string-is-basically-an-integer-in-quotes-using-rub lesy

Http://rubular.com/ (интерактивный редактор регулярных выражений Ruby)

Если есть области, которые, по вашему мнению, можно улучшить, дайте мне знать в разделе комментариев ниже. Мы будем благодарны за любые отзывы!