I'm trying to catch the difference between a numeric string vs. an arbitrary string:
'0'.to_f
#=> 0.0
'hello'.to_f
#=> 0.0
Both of the above return a Float. How do I catch the difference if the user inputs the actual value '0' or if the user inputs the value 'hello'?
I am trying to create a simple Celsius to Fahrenheit calculator. If the user inputs "hello" the program should output Please type in a number: but if the user types in 0 then the program should output the correct Fahrenheit calculation.
Use this:
number = Float( string_to_convert ) rescue nil
if number.nil?
puts "#{string_to_convert} is not a number"
else
# DO the conversion
end
This will use a sensible set of rules for converting String values into Floats, supporting negative numbers, scientific notation, whilst not requiring you write a regular expression to try and capture all the valid ways of expressing floating point numbers in Ruby.
The rescue is required to catch the error from a failed conversion.
Potentially better Ruby code for your particular purpose (and combining design from Tamer's answer with feedback from Stefan in comments):
begin
number = Float( string_to_convert )
rescue ArgumentError
puts "'#{string_to_convert}' is not a number"
else
# Do the conversion, using number variable
end
However, if the program flow is more complicated than input-or-bust-then-repeat, I still find the one-liner can be useful - provided of course you either raise an error later or can deal with missing values because the conversion to a Float failed.
You could use a regular expression like:
/
\A # anchor on start of a string, so there there is nothing more than float allowed
\-? # optional minus
\d+ # non-empty row of digits
(
\. # dot
\d+ # another row of digits
)? # ? means both of the above are optional
\z # anchor on end of a string, so there there is nothing more than float allowed
/x
single line version: /\A\-?\d+(\.\d+)?\z/
In typical use-case, Float( ) might be better, but using a regex separates you from Ruby's definition of Float literal, that can come handy if i.e. you want to allow a comma as decimal mark, or thousand separator (Ruby will only allow . and _ respectively). Regex match will also just return true or false, that can be useful if you want to avoid exception handling - ArgumentError thrown by Float( ) on fail is pretty generic, so it might get thrown by other nearby method call, and thus can get difficult to handle properly, or just make your code ugly.
begin
value = Float(input)
# do your correct logic here
rescue ArgumentError
puts "Please type in a number!"
end
Related
Theoretical question
I'm trying to find new practical ways to convert integers into strings and the other way around.
I only know the .to_s ; .to_i ; .to_f methods and would like to know if there are other ways to do to it without writing + to put together the variables. For example:
var1 = 16
puts 'I\'m ' + var1.to_s + ' years old.'
In longer codes is getting tiring writing all this to just convert a integer to a string.
By the way I also found this Timer program here on Stack and the #{ is an example of what I'm trying to do. Adding an integer to a string without + and .to_s But I don't know how it works.
30.downto(0) do |i|
puts "00:00:#{'%02d' % i}"
sleep 1
end
Thank you in advance for the suggestions!
Ruby has a pretty powerful string interpolator feature using #{...} where that can contain fairly arbitrary Ruby code. The end result is always converted to a string using, effectively, to_s.
That is you can do this:
puts "00:00:#{'%02d' % i}"
Where that gets stringified and then interpolated.
This is roughly the same as:
i_str = '%02d' % i
puts "00:00:#{i_str}"
Where that is effectively:
i_str = '%02d' % i
puts "00:00:%s" % i_str
You could also combine that into a single operation:
puts "00:00:%02d" % i
Where you generally use interpolation or sprintf-style template strings, not both at the same time. It keeps your code cleaner since only one mechanism is in play.
The only reason .to_s is needed when doing concatenation is Ruby is very particular about "adding" together two things. x + y has a completely different outcome depending on what x and y are.
Consider:
# Integer + Integer (Integer#+)
1 + 2
# => 3
# Array + Array (Array#+)
[ 1 ] + [ 2 ]
# => [1,2]
# String + String (String#+)
"1" + "2"
# => "12"
Note that in each case it's actually a different method being called, and the general form of x + y is:
x.send(:+, y)
So it's actually a method call, and as such, each method may impose restrictions on what it can operate on by emitting exceptions if it can't or won't deal.
It's called string interpolation. For example:
puts "I\'m #{var1} years old."
The way it works is this:
You have to enclose the string in double quotes, not single quotes.
You put your variable inside this: #{}, e.g. "#{variable}".
This will always convert non-string variables into strings, and plug (i.e. interpolate) them into the surrounding string.
My ruby command is,
"980,323,344.00".to_i
Why does it return 980 instead of 980323344?
You can achieve it by doing this :
"980,323,344.00".delete(',').to_i
The reason your method call to to_i does not return as expected is explained here, and to quote, the method :
Returns the result of interpreting leading characters in str as an integer base base (between 2 and 36). Extraneous characters past the end of a valid number are ignored.
Extraneous characters in your case would be the comma character that ends at 980, the reason why you see 980 being returned
In ruby calling to_i on a string will truncate from the beginning of a string where possible.
number_string = '980,323,344.00'
number_string.delete(',').to_i
#=> 980323344
"123abc".to_i
#=> 123
If you want to add underscores to make longer number more readable, those can be used where the conventional commas would be in written numbers.
"980_323_344.00".to_i
#=> 980323344
The documentation for to_i might be a bit misleading:
Returns the result of interpreting leading characters in str as an integer base base (between 2 and 36)
"interpreting" doesn't mean that it tries to parse various number formats (like Date.parse does for date formats). It means that it looks for what's a valid integer literal in Ruby (in the given base). For example:
1234. #=> 1234
'1234'.to_i #=> 1234
1_234. #=> 1234
'1_234'.to_i. #=> 1234
0d1234 #=> 1234
'0d1234'.to_i #=> 1234
0x04D2 #=> 1234
'0x04D2'.to_i(16) #=> 1234
Your input as a whole however is not a valid integer literal: (Ruby doesn't like the ,)
980,323,344.00
# SyntaxError (syntax error, unexpected ',', expecting end-of-input)
# 980,323,344.00
# ^
But it starts with a valid integer literal. And that's where the the seconds sentence comes into play:
Extraneous characters past the end of a valid number are ignored.
So the result is 980 – the leading characters which form a valid integer converted to an integer.
If your strings always have that format, you can just delete the offending commas and run the result through to_i which will ignore the trailing .00:
'980,323,344.00'.delete(',') #=> "980323344.00"
'980,323,344.00'.delete(',').to_i #=> 980323344
Otherwise you could use a regular expression to check its format before converting it:
input = '980,323,344.00'
number = case input
when /\A\d{1,3}(,\d{3})*\.00\z/
input.delete(',').to_i
when /other format/
# other conversion
end
And if you are dealing with monetary values, you should consider using the money gem and its monetize addition for parsing formatted values:
amount = Monetize.parse('980,323,344.00')
#=> #<Money fractional:98032334400 currency:USD>
amount.format
#=> "$980.323.344,00"
Note that format requires i18n so the above example might require some setup.
I can convert an object to an integer with to_i. How can I get it to not convert if the input is not a number?
In python I would do this:
h=input("Number: ")
try:
h=int(h)
except ValueError:
print("Please enter numbers!")
So I tried this in ruby:
print "Number: "
h=gets.chomp
try(h.to_i)
print(h)
end
but it prints the input even if I enter letters, so this means I am doing it wrong. What is the correct Ruby way of doing this?
Reading ruby docs for to_i
Returns the result of interpreting leading characters in str as an
integer base base (between 2 and 36). Extraneous characters past the
end of a valid number are ignored. If there is not a valid number at
the start of str, 0 is returned. This method never raises an exception
when base is valid.
What you want in your case (to map the Python behavior) is:
begin
Integer(gets)
rescue ArgumentError
puts "Please enter numbers!"
end
I am writing a 6502 assembler in Ruby. I am looking for a way to validate hexadecimal operands in string form. I understand that the String object provides a "hex" method to return a number, but here's a problem I run into:
"0A".hex #=> 10 - a valid hexadecimal value
"0Z".hex #=> 0 - invalid, produces a zero
"asfd".hex #=> 10 - Why 10? I guess it reads 'a' first and stops at 's'?
You will get some odd results by typing in a bunch of gibberish. What I need is a way to first verify that the value is a legit hex string.
I was playing around with regular expressions, and realized I can do this:
true if "0A" =~ /[A-Fa-f0-9]/
#=> true
true if "0Z" =~ /[A-Fa-f0-9]/
#=> true <-- PROBLEM
I'm not sure how to address this issue. I need to be able to verify that letters are only A-F and that if it is just numbers that is ok too.
I'm hoping to avoid spaghetti code, riddled with "if" statements. I am hoping that someone could provide a "one-liner" or some form of elegent code.
Thanks!
!str[/\H/] will look for invalid hex values.
String#hex does not interpret the whole string as hex, it extracts from the beginning of the string up to as far as it can be interpreted as hex. With "0Z", the "0" is valid hex, so it interpreted that part. With "asfd", the "a" is valid hex, so it interpreted that part.
One method:
str.to_i(16).to_s(16) == str.downcase
Another:
str =~ /\A[a-f0-9]+\Z/i # or simply /\A\h+\Z/ (see hirolau's answer)
About your regex, you have to use anchors (\A for begin of string and \Z for end of string) to say that you want the full string to match. Also, the + repeats the match for one or more characters.
Note that you could use ^ (begin of line) and $ (end of line), but this would allow strings like "something\n0A" to pass.
This is an old question, but I just had the issue myself. I opted for this in my code:
str =~ /^\h+$/
It has the added benefit of returning nil if str is nil.
Since Ruby has literal hex built-in, you can eval the string and rescue the SyntaxError
eval "0xA" => 10
eval "0xZ" => SyntaxError
You can use this on a method like
def is_hex?(str)
begin
eval("0x#{str}")
true
rescue SyntaxError
false
end
end
is_hex?('0A') => true
is_hex?('0Z') => false
Of course since you are using eval, make sure you are sending only safe values to the methods
I have a string that is one character long and can be any possible character value:
irb(main):001:0> "\x0"
=> "\u0000"
I thought this might work:
irb(main):002:0> "\x0" += 1
SyntaxError: (irb):2: syntax error, unexpected tOP_ASGN, expecting $end
"\x0" += 1
^ from /opt/rh/ruby193/root/usr/bin/irb:12:in `<main>'
But, as you can see, it didn't. How can I increment/decrement my character?
Edit:
Ruby doesn't seem to be set up to do this. Maybe I'm approaching this the wrong way. I want to manipulate raw data in terms of 8-bit chunks. How can I best accomplish that sort of operation?
Depending on what the possible values are, you can use String#next:
"\x0".next
# => "\u0001"
Or, to update an existing value:
c = "\x0"
c.next!
This may well not be what you want:
"z".next
# => "aa"
The simplest way I can think of to increment a character's underlying codepoint is this:
c = 'z'
c = c.ord.next.chr
# => "{"
Decrementing is slightly more complicated:
c = (c.ord - 1).chr
# => "z"
In both cases there's the assumption that you won't step outside of 0..255; you may need to add checks for that.
You cannot do:
"\x0" += 1
Because, in Ruby, that is short for:
"\x0" = "\x0" + 1
and it is a syntax error to assign a value to a string literal.
However, given an integer n, you can convert it to a character by using pack. For example,
[97].pack 'U' # => "a"
Similarly, you can convert a character into an integer by using ord. For example:
[300].pack('U').ord # => 300
With these methods, you can easily write your own increment function, as follows:
def step(c, delta=1)
[c.ord + delta].pack 'U'
end
def increment(c)
step c, 1
end
def decrement(c)
step c, -1
end
If you just want to manipulate bytes, you can use String#bytes, which will give you an array of integers to play with. You can use Array#pack to convert those bytes back to a String. (Refer to documentation for encoding options.)
You could use the String#next method.
I think the most elegant method (for alphanumeric chars) would be:
"a".tr('0-9a-z','1-9a-z0')
which would loop the a through to z and through the numbers and back to a.
I reread the question and see, that my answer has nothing to do with the question. I have no answer for manipulationg 8-bit values directly.