Як перевірити, чи рядок в основному є цілим числом лапок за допомогою Ruby


128

Мені потрібна функція is_an_integer, де

  • "12".is_an_integer? повертає правду.
  • "blah".is_an_integer? повертає помилкове.

Як я можу це зробити в Ruby? Я б написав регулярний вираз, але я припускаю, що для цього є помічник, про який я не знаю.



1
Будьте обережні, використовуючи рішення, спираючись на регулярні вирази. Тести показують, що вони працюють набагато повільніше, ніж звичайний код.
Олов'яний чоловік

Відповіді:


135

Можна використовувати регулярні вирази. Ось функція з пропозиціями @ janm.

class String
    def is_i?
       !!(self =~ /\A[-+]?[0-9]+\z/)
    end
end

Відредагована версія згідно з коментарем @wich:

class String
    def is_i?
       /\A[-+]?\d+\z/ === self
    end
end

У випадку, якщо вам потрібно лише перевірити додатні цифри

  if !/\A\d+\z/.match(string_to_check)
      #Is not a positive number
  else
      #Is all good ..continue
  end  

4
Непогано. У Ruby зазвичай пропускають ключове слово "return", якщо значення повернення генерується в останньому виразі функції. Це також поверне ціле значення нуля, ви, мабуть, хочете булеве значення, тому щось подібне !! (str = ~ / ^ [- +]? [0-9] + $ /) зробить це. Тоді ви можете додати його до String і залишити аргумент, використовуючи "self" замість "str", і тоді ви можете змінити ім'я на "is_i?" ...
janm

2
Дякую! У мене немає абсолютно ніякого поняття про рубінові конвенції та практики. Я просто зробив швидкий google на рубіні та регулярних виразах, щоб побачити синтаксис, змінив регулярний вираз, щоб застосувати до проблеми, і перевірив її. Насправді це дуже акуратно. Можливо, мені доведеться придивитись довше, коли у мене буде більше вільного часу.
Радо

Ви маєте правильну ідею, але вона не відповідає двійковій чи шістнадцятковій літералі (див. Моє відредаговане рішення нижче).
Сара Мей

16
Два коментарі. Ви можете використовувати /regexp/ === selfзамість !!(self =~ /regexp/)конструкції. Ви можете використовувати клас символів «\ d» замість[0-9]
яким

1
Найпростіший регулярний вираз для цілого числа, ймовірно, є / ^ \ d + $ /
keithxm23

169

Ну ось простий спосіб:

class String
  def is_integer?
    self.to_i.to_s == self
  end
end

>> "12".is_integer?
=> true
>> "blah".is_integer?
=> false

Я не згоден з рішеннями, які провокують виняток для перетворення рядка - винятки не є контролем потоку, і ви також можете зробити це правильно. Враховуючи це, моє рішення вище не стосується цілих чисел без бази-10. Отже, ось як це зробити, не вдаючись до винятків:

  class String
    def integer? 
      [                          # In descending order of likeliness:
        /^[-+]?[1-9]([0-9]*)?$/, # decimal
        /^0[0-7]+$/,             # octal
        /^0x[0-9A-Fa-f]+$/,      # hexadecimal
        /^0b[01]+$/              # binary
      ].each do |match_pattern|
        return true if self =~ match_pattern
      end
      return false
    end
  end

27
"01" .to_i.to_s! = "01"
sepp2k

2
Чи не могли б ви замінити self.to_i.to_s == selfз Integer self rescue false?
Мередіт Л. Паттерсон

5
Можна, але це було б поганою формою. Ви не використовуєте винятки як контрольний потік, і нічий код ніколи не повинен містити "false false" (або "true true"). Деякий простий gsub'ing змусив би моє рішення працювати на крайніх випадках, не визначених ОП.
Сара Мей

4
Я знаю, що багато людей його використовують, і це, звичайно, естетично. Для мене, хоча це є вказівкою на те, що код потребує перебудови. Якщо ви очікуєте винятку ... це не виняток.
Сара Мей

2
Я згоден, що винятки не повинні використовуватися як контрольний потік. Я не думаю, що вимога полягає у визнанні номерів, орієнтованих на розробників. У ситуаціях, які не є програмістами, їх можна розглядати як помилку, особливо з огляду на можливу плутанину навколо провідних нулів та восьмери. Також не відповідає to_i. Ваш код не обробляє справу "-0123". Як тільки ви впораєтеся з цим випадком, вам не знадобиться окремий регулярний вираз для восьмеричного. Можна просто далі, використовуючи "будь-який?". Єдиним твердженням у вашій функції може бути "[/ re1 /, / re2 /, / re3 /] .any? {| Re | self = ~ re}", без жодних пунктів або повернень.
janm

67

Ви можете використовувати Integer(str)та бачити, чи підвищується він:

def is_num?(str)
  !!Integer(str)
rescue ArgumentError, TypeError
  false
end

Слід зазначити, що хоча це справді повертає істину для "01", це не робиться "09", просто тому, 09що не було б дійсним цілим числовим буквалом. Якщо це не поведінка, яку ви хочете, ви можете додати 10як другий аргумент Integer, тому число завжди інтерпретується як базове 10.


39
Чувак ... провокує виняток просто для перетворення числа? Винятки не є контрольним потоком.
Сара Мей

29
Вони не є, але, на жаль, це канонічний спосіб визначити "цілісність" рядка в Ruby. Методи, які використовують #to_i, просто надто порушені через вседозволеність.
Авді

17
Для тих, хто цікавиться, чому Integer ("09") недійсний, оскільки "0" робить його восьмеричним, а 9 - не дійсним восьмеричним числом. osdir.com/ml/lang.ruby.general/2002-08/msg00247.html
Ендрю Грімм

20
Сара: ви можете використовувати Regex, але для того, щоб вирішити всі випадки, які робить Рубі під час розбору цілих чисел (від'ємні числа, шістнадцятковий, восьмеричний, підкреслення, наприклад, 1_000_000), це було б дуже великим Regex і легко помилитися. Integer()є канонічним, оскільки Integer ()ви точно знаєте, що все, що Рубі вважає цілим літералом, буде прийнято, а все інше буде відхилено. Дублювання того, що мова вже дає вам, можливо, гірший запах коду, ніж використання виключень для контролю.
Авді

9
@Rado Так відновлює колесо.
sepp2k

24

Ви можете зробити один вкладиш:

str = ...
int = Integer(str) rescue nil

if int
  int.times {|i| p i}
end

або навіть

int = Integer(str) rescue false

Залежно від того, що ви намагаєтесь зробити, ви також можете безпосередньо використати блок початкового кінця із рятувальним пунктом:

begin
  str = ...
  i = Integer(str)

  i.times do |j|
    puts j
  end
rescue ArgumentError
  puts "Not an int, doing something else"
end

1
Що стосується теми "Виняток як контрольний потік": оскільки ми не знаємо, як застосовується даний метод, ми не можемо реально судити, чи підходять винятки чи ні. Якщо рядок вводиться і він повинен бути цілим числом, то надання не цілого числа гарантує виняток. Хоча, можливо, обробка не є одним і тим же методом, і ми, ймовірно, просто робимо Integer (str) .times {| i | ставить i} або що завгодно.
Роберт Клемме

24
"12".match(/^(\d)+$/)      # true
"1.2".match(/^(\d)+$/)     # false
"dfs2".match(/^(\d)+$/)    # false
"13422".match(/^(\d)+$/)   # true

4
Він не повертається trueі , falseале MatchDataекземпляри іnil
Стефану

Це не те, що воно повертає, але якщо воно відповідає
Мацей Красовський

5
Загорніть його !!або використовуйте, present?якщо вам потрібен булевий !!( "12".match /^(\d)+$/ )або "12".match(/^(\d)+$/).present?(для цього потрібні Rails / activesupport)
mahemoff

1
Цей регулярний вираз не враховує ознаки: від’ємні числа теж є дійсними цілими числами . Тепер ви перевіряєте дійсні натуральні числа чи нуль.
Йохем Шуленклоппер

13

Ruby 2.6.0 дозволяє передати ціле число без винятку , і повернеться, nilякщо команда не вдасться. А оскільки в nilосновному поводиться як falseу Ruby, ви можете легко перевірити наявність цілого числа, як-от так:

if Integer(my_var, exception: false)
  # do something if my_var can be cast to an integer
end

8
class String
  def integer?
    Integer(self)
    return true
  rescue ArgumentError
    return false
  end
end
  1. Це не з префіксом is_. Я вважаю це дурним щодо методів знаків питання, мені подобається "04".integer?набагато краще, ніж "foo".is_integer?.
  2. Він використовує розумне рішення sepp2k, яке передає "01"і таке.
  3. Об'єктно орієнтований, так.

1
+1 за його ім'я #integer ?, -1 за захаращення Рядок з ним :-P
Авді

1
Куди б іще? integer?("a string")ftl.
Серпень Ліллеас

2
String#integer?- це загальний патч, який кожен кодер Ruby та їх двоюрідний брат любить додавати до мови, що веде до кодових баз з трьома різними невідповідними реалізаціями та несподіваним злому. Я навчився цьому важким шляхом у великих проектах Ruby.
Авді

Той самий коментар, що і вище: винятки не слід використовувати для контролю потоку.
Сара Мей

Знизу: це рішення витрачає одну конверсію.
Роберт Клемме

7

Найкращий і простий спосіб - це використання Float

val = Float "234" rescue nil

Float "234" rescue nil #=> 234.0

Float "abc" rescue nil #=> nil

Float "234abc" rescue nil #=> nil

Float nil rescue nil #=> nil

Float "" rescue nil #=> nil

Integerтакож добре, але це повернеться 0заInteger nil


Я радий, що помітив твою відповідь. Інакше я пішов би з "Integer", коли мені знадобився "Float".
Джефф Живкович

Це проста, але найкраща відповідь! У більшості випадків нам не потрібні вигадливі виправлення до класу String. Це найкраще працює для мене!
Анх Нгуен

(Float (значення) рятування помилково)? Float (значення) .to_s == значення? Float (значення): Integer (value): value
okliv

6

Я віддаю перевагу:

config / ініціалізатори / string.rb

class String
  def number?
    Integer(self).is_a?(Integer)
  rescue ArgumentError, TypeError
    false
  end
end

і потім:

[218] pry(main)> "123123123".number?
=> true
[220] pry(main)> "123 123 123".gsub(/ /, '').number?
=> true
[222] pry(main)> "123 123 123".number?
=> false

або перевірити номер телефону:

"+34 123 456 789 2".gsub(/ /, '').number?

4

Набагато простіший спосіб міг бути

/(\D+)/.match('1221').nil? #=> true
/(\D+)/.match('1a221').nil? #=> false
/(\D+)/.match('01221').nil? #=> true

3
  def isint(str)
    return !!(str =~ /^[-+]?[1-9]([0-9]*)?$/)
  end

Відповіді з коду лише не дуже корисні. Натомість надайте пояснення, як це працює і чому це відповідна відповідь. Ми хочемо просвітитись на майбутнє, щоб рішення було зрозумілим, а не вирішеним найближчим питанням.
Олов'яний чоловік

3

Особисто мені подобається підхід до винятку, хоча я би зробив це трохи більш коротко:

class String
  def integer?(str)
    !!Integer(str) rescue false
  end
end

Однак, як уже зазначали інші, це не працює з Octal рядками.


2

Ruby 2.4 має Regexp#match?: (з a ?)

def integer?(str)
  /\A[+-]?\d+\z/.match? str
end

Для старих версій Ruby є Regexp#===. І хоча, як правило, слід уникати прямого використання оператора рівності випадків, це виглядає дуже чисто:

def integer?(str)
  /\A[+-]?\d+\z/ === str
end

integer? "123"    # true
integer? "-123"   # true
integer? "+123"   # true

integer? "a123"   # false
integer? "123b"   # false
integer? "1\n2"   # false

2

Це може бути не підходить для всіх випадків, просто використовуючи:

"12".to_i   => 12
"blah".to_i => 0

може також зробити для деяких.

Якщо це число, а не 0, воно поверне число. Якщо він повертає 0, це або рядок, або 0.


11
Працює , але це не рекомендується, так як "12blah".to_i => 12. Це може спричинити певні проблеми в дивних сценаріях.
rfsbraz

2

Ось моє рішення:

# /initializers/string.rb
class String
  IntegerRegex = /^(\d)+$/

  def integer?
    !!self.match(IntegerRegex)
  end
end

# any_model_or_controller.rb
'12345'.integer? # true
'asd34'.integer? # false

А ось як це працює:

  • /^(\d)+$/це вираження регулярного вираження для знаходження цифр у будь-якому рядку. Ви можете перевірити свої вирази та результати регулярного вираження на веб-сайті http://rubular.com/ .
  • Ми зберігаємо його в постійному режимі, IntegerRegexщоб уникнути зайвого розподілу пам'яті кожного разу, коли ми використовуємо його в методі.
  • integer?це запитувальний метод, який повинен повернутись trueабо false.
  • matchце метод на рядок, який відповідає входженням відповідно до заданого вираження регулярного вираження в аргументі і повертає відповідні значення або nil.
  • !!перетворює результат matchметоду в еквівалентний булевий.
  • А декларація методу в існуючому Stringкласі - це патч мавпи, який нічого не змінює в існуючих функціях String, а просто додає інший метод, названий integer?на будь-якому об'єкті String.

1
Чи можете ви додати трохи пояснення до цього, будь ласка?
stef

@stef - я зробив те саме. Будь ласка, дайте мені знати, якщо у вас ще є запит.
Сачин

1

Якщо розгорнути відповідь на @ rado вище, можна також використати потрійне твердження, щоб примусити повернути справжні чи помилкові булеви без використання подвійних чубків. Зрозуміло, що версія подвійного логічного заперечення є більш короткою, але, мабуть, важче читати для новачків (як я).

class String
  def is_i?
     self =~ /\A[-+]?[0-9]+\z/ ? true : false
  end
end

Врахуйте, що використання регулярних виразів змушує Рубі зробити набагато більше роботи, тому, якщо це буде використано в циклі, це сповільнить код. Закріплення виразу допомагає, але виразка все ще значно повільніше.
Олов'яний чоловік

1

Для більш узагальнених випадків (включаючи числа з десятковою комою) можна спробувати наступний метод:

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

Ви можете протестувати цей метод у сеансі irb:

(irb)
>> number?(7)
=> #<MatchData "7" 1:nil>
>> !!number?(7)
=> true
>> number?(-Math::PI)
=> #<MatchData "-3.141592653589793" 1:".141592653589793">
>> !!number?(-Math::PI)
=> true
>> number?('hello world')
=> nil
>> !!number?('hello world')
=> false

Для детального пояснення регексу, що тут стосується, перегляньте цю статтю в блозі :)


Не потрібно дзвонити, obj.is_a? Stringтому що рядок # to_s повернеться сам, що, напевно, не вимагає занадто багато обробки в порівнянні з .is_a?викликом. Таким чином, ви будете здійснювати лише один дзвінок у цій лінії замість одного чи двох. Крім того, ви можете включити безпосередньо !!всередину number?методу, оскільки, за умовою, ім'я методу, яке закінчується ?, повинно повернути булеве значення. З повагою!
Джованні Бенуссі

-1

Мені подобається наступне, прямо вперед:

def is_integer?(str)
  str.to_i != 0 || str == '0' || str == '-0'
end

is_integer?('123')
=> true

is_integer?('sdf')
=> false

is_integer?('-123')
=> true

is_integer?('0')
=> true

is_integer?('-0')
=> true

Але обережно:

is_integer?('123sdfsdf')
=> true

-2

Один лайнер в string.rb

def is_integer?; true if Integer(self) rescue false end

-3

Я не впевнений, чи було це питання, коли задають це питання, але для тих, хто натрапляє на цю посаду, найпростіший спосіб:

var = "12"
var.is_a?(Integer) # returns false
var.is_a?(String) # returns true

var = 12
var.is_a?(Integer) # returns true
var.is_a?(String) # returns false

.is_a? буде працювати з будь-яким об’єктом.


Це не те, що задається оригінальним питанням. OP хоче знати, чи рядок також є цілим числом. наприклад "12".is_an_integer? == true "not12".is_an_integer? == false 12.is_an_integer? == true
Марклер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.