रूबी में सुरक्षित पूर्णांक पार्सिंग


160

मेरे पास एक स्ट्रिंग है, कहते हैं '123', और मैं इसे पूर्णांक में बदलना चाहता हूं 123

मुझे पता है कि आप बस कर सकते हैं some_string.to_i, लेकिन यह 'lolipops'करने के लिए धर्मान्तरित है 0, जो मेरे मन में प्रभाव नहीं है। मैं चाहता हूं कि जब मैं कुछ अच्छा और दर्दनाक के साथ कुछ अमान्य करने की कोशिश करता हूं, तो यह मेरे चेहरे पर उड़ जाएगा Exception। अन्यथा, मैं एक मान्य 0और कुछ के बीच अंतर नहीं कर सकता जो कि बस एक संख्या नहीं है।

EDIT: मैं इसे करने के मानक तरीके की तलाश कर रहा था, रेगेक्स ट्रिकरी के बिना।

जवाबों:


234

रूबी में यह कार्यक्षमता निर्मित है:

Integer('1001')                                    # => 1001  
Integer('1001 nights')  
# ArgumentError: invalid value for Integer: "1001 nights"  

जैसा कि जोसेफ पेकोरो के जवाब में कहा गया है , आप ऐसे स्ट्रिंग को देखना चाहते हैं जो वैध नॉन-दशमलव संख्याएँ हों, जैसे कि 0xहेक्स के लिए और 0bबाइनरी के लिए शुरू होने वाले , और संभावित रूप से अधिक ट्रिकी संख्या शून्य से शुरू होती है जिसे ऑक्टल के रूप में पार्स किया जाएगा।

रूबी 1.9.2 ने मूलांक के लिए वैकल्पिक दूसरा तर्क जोड़ा ताकि उपरोक्त समस्या से बचा जा सके:

Integer('23')                                     # => 23
Integer('0x23')                                   # => 35
Integer('023')                                    # => 19
Integer('0x23', 10)
# => #<ArgumentError: invalid value for Integer: "0x23">
Integer('023', 10)                                # => 23

27

यह काम कर सकता है:

i.to_i if i.match(/^\d+$/)

8
PSA: रूबी में, ^और सबसे अलग regexp जायके की तुलना में मेटाचार्स के रूप में $ अलग-अलग अर्थ है। आप शायद उपयोग करने के लिए \Aऔर \Zइसके बजाय मतलब है ।
पीजे

1
पांडित्यपूर्ण होने के लिए, @pje के अनुसार विभिन्न रेगेक्स एंकरों का उल्लेख वांछित व्यवहार के आधार पर गलत हो सकता है। इसके बजाय पूंजीकृत Z एंकर के विवरण \zके \Zरूप में उपयोग करने पर विचार करें : "स्ट्रिंग का अंत मेल खाता है। यदि स्ट्रिंग एक नई रेखा के
डेल

24

इसके अलावा उन प्रभावों से अवगत रहें जो वर्तमान स्वीकृत समाधान हेक्सिंग, ओक्साल और बाइनरी नंबरों को पार्स करने पर हो सकते हैं:

>> Integer('0x15')
# => 21  
>> Integer('0b10')
# => 2  
>> Integer('077')
# => 63

रूबी नंबरों में जो हेक्स के साथ शुरू होते हैं 0xया 0Xहोते हैं, 0bया 0Bबाइनरी होते हैं, और बस 0ओक्टल होते हैं। यदि यह वांछित व्यवहार नहीं है, तो आप इसे कुछ अन्य समाधानों के साथ जोड़ सकते हैं जो यह जांचते हैं कि क्या स्ट्रिंग पहले एक पैटर्न से मेल खाती है। जैसे /\d+/नियमित भाव, आदि।


1
यही कारण है कि मैं हालांकि रूपांतरण से उम्मीद करूँगा
wvdschel

5
रूबी 1.9 में, आप दूसरे तर्क के रूप में आधार पास कर सकते हैं।
एंड्रयू ग्रिम

17

स्वीकृत समाधान के साथ एक और अप्रत्याशित व्यवहार (1.8, 1.9 के साथ ठीक है):

>> Integer(:foobar)
=> 26017
>> Integer(:yikes)
=> 26025

इसलिए यदि आप सुनिश्चित नहीं हैं कि क्या पारित किया जा रहा है, तो सुनिश्चित करें कि आप एक जोड़ते हैं .to_s


7
रूबी में परीक्षण 1.9। Integer (: foobar) => प्रतीक को Integer में
टाइप

9

मुझे मायरोन का जवाब पसंद है लेकिन यह "मैं अब जावा / सी # का उपयोग नहीं करता हूं, इसलिए मैं रूबी रोग से पीड़ित हूं, इसलिए मैं फिर से विरासत का उपयोग नहीं करने जा रहा हूं" । किसी भी वर्ग को खोलना खतरे से भरा हो सकता है और इसे संयम से इस्तेमाल किया जाना चाहिए, खासकर जब यह रूबी की मुख्य लाइब्रेरी का हिस्सा हो। मैं यह नहीं कह रहा हूं कि इसका कभी उपयोग न करें, लेकिन आमतौर पर इससे बचना आसान है और बेहतर विकल्प उपलब्ध हैं, जैसे

class IntegerInString < String

  def initialize( s )
    fail ArgumentError, "The string '#{s}' is not an integer in a string, it's just a string." unless s =~ /^\-?[0-9]+$/
    super
  end
end

फिर जब आप एक स्ट्रिंग का उपयोग करना चाहते हैं जो एक संख्या हो सकती है यह स्पष्ट है कि आप क्या कर रहे हैं और आप किसी भी कोर क्लास को क्लोब नहीं करते हैं, जैसे;

n = IntegerInString.new "2"
n.to_i
# => 2

IntegerInString.new "blob"
ArgumentError: The string 'blob' is not an integer in a string, it's just a string.

आप इनिशियलाइज़ में सभी प्रकार के चेक जोड़ सकते हैं, जैसे कि बाइनरी नंबर आदि के लिए जाँच। मुख्य बात यह है कि रूबी लोगों के लिए है और लोगों के लिए होने का मतलब स्पष्टता है । किसी वस्तु को उसके परिवर्तनशील नाम और उसके वर्ग नाम के माध्यम से नाम देने से चीजें बहुत स्पष्ट हो जाती हैं।


6

मुझे अपनी अंतिम परियोजना में इससे निपटना था, और मेरा कार्यान्वयन समान था, लेकिन थोड़ा अलग:

class NotAnIntError < StandardError 
end

class String
  def is_int?    
    self =~ /^-?[0-9]+$/
  end

  def safe_to_i
    return self.to_i if is_int?
    raise NotAnIntError, "The string '#{self}' is not a valid integer.", caller
  end
end

class Integer
  def safe_to_i
    return self
  end            
end

class StringExtensions < Test::Unit::TestCase

  def test_is_int
    assert "98234".is_int?
    assert "-2342".is_int?
    assert "02342".is_int?
    assert !"+342".is_int?
    assert !"3-42".is_int?
    assert !"342.234".is_int?
    assert !"a342".is_int?
    assert !"342a".is_int?
  end

  def test_safe_to_i
    assert 234234 == 234234.safe_to_i
    assert 237 == "237".safe_to_i
    begin
      "a word".safe_to_i
      fail 'safe_to_i did not raise the expected error.'
    rescue NotAnIntError 
      # this is what we expect..
    end
  end

end

2
someString = "asdfasd123"
number = someString.to_i
if someString != number.to_s
  puts "oops, this isn't a number"
end

शायद ऐसा करने का सबसे साफ तरीका नहीं है, लेकिन काम करना चाहिए।


1

पुन: क्रिस का जवाब

आपका कार्यान्वयन "1a" या "b2" जैसी चीजों के माध्यम से चलो। इसके बजाय इसके बारे में कैसे:

def safeParse2(strToParse)
  if strToParse =~ /\A\d+\Z/
    strToParse.to_i
  else
    raise Exception
  end
end

["100", "1a", "b2", "t"].each do |number|
  begin
    puts safeParse2(number)
  rescue Exception
    puts "#{number} is invalid"
  end
end

यह आउटपुट:

100
1a is invalid
b2 is invalid
t is invalid

पांडित्यपूर्ण होने के लिए, @pje के अनुसार अलग-अलग रेगेक्स एंकरों का उल्लेख और उपयोग किया जाना वांछित व्यवहार के आधार पर गलत हो सकता है। इसके बजाय पूंजीकृत Z एंकर के विवरण \zके \Zरूप में उपयोग करने पर विचार करें : "स्ट्रिंग का अंत मेल खाता है। यदि स्ट्रिंग एक नई रेखा के
डेल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.