to_i never fails; Integer() does¶
Level: 101 · anyone turning user input into a number
One line: "42abc".to_i is 42 and "abc".to_i is 0, with no error; Integer("42abc") raises, reads 0x, 0b and a leading 0 the way a Ruby literal would, and — like to_i — does not read the Arabic-Indic digits that Python's int() accepts.
Verified output of to_i_never_fails_rb.rb — regenerated by tools/run_examples.py, never hand-typed.
input to_i Integer()
"42" 42 42
" 42\n" 42 42
"42abc" 42 ArgumentError
"abc" 0 ArgumentError
"" 0 ArgumentError
"0x1A" 0 26
"0b101" 0 5
"010" 10 8
"1_000" 1000 1000
"1__000" 1 ArgumentError
"4 2" 4 ArgumentError
"١٢" 0 ArgumentError
Integer("abc", exception: false) nil
Integer("010", 10) 10
"0x1A".hex 26
"1.5 kg".to_f 1.5
"1,5".to_f 1.0
Float("1.") 1.0
Reading the output¶
to_ireads leading digits and stops. Whitespace in front is skipped, and anything that is not a digit ends the number.""and"abc"give 0, which cannot be told apart from"0".Integer()takes the whole string or raises. Surrounding whitespace is allowed; anything else isArgumentError.Integer()reads prefixes like a literal."0x1A"is 26 and"0b101"is 5, whereto_isees 0."010"is 8 — octal — whileto_isays 10, so a zero-padded id changes value depending on which one parsed it.Integer("010", 10)insists on decimal.- One underscore between digits is allowed, as in a literal; two are not, and
to_istops at them. exception: falsemakesInteger()returnnilinstead of raising.to_fis as lenient asto_i, and knows nothing about locales:"1,5".to_fis1.0.Float("1.")is accepted since Ruby 3.4.
What to do¶
Parse input with Integer(s, 10) — or Integer(s, 10, exception: false) and a check for nil — so that a typo is an error and a leading zero is not octal. Keep to_i for strings you produced yourself.
If you are coming from another language¶
Perl numifies a string the way to_i reads it — leading number, no exception — and warns under use warnings when there was anything else in the string:
Verified output of to_i_never_fails_pl.pl — regenerated by tools/run_examples.py, never hand-typed.
"42" + 0 42
" 42\n" + 0 42
"42abc" + 0 42 warning: isn't numeric
"abc" + 0 0 warning: isn't numeric
"0x1A" + 0 0 warning: isn't numeric
"1_000" + 0 1 warning: isn't numeric
"١٢" + 0 0 warning: isn't numeric
"0x1A" is 0 to Perl as well (its hex function reads it), and "1_000" is 1: underscores belong to Perl's literals, not to its strings. The Perl library's Numbers from text ↗ goes further, into "nan" and "inf".
Python's int() is strict like Integer(), but reads every Unicode decimal digit and needs base 0 before it accepts a prefix:
Verified output of to_i_never_fails_py.py — regenerated by tools/run_examples.py, never hand-typed.
'42' int(s) 42
' 42\n' int(s) 42
'42abc' int(s) ValueError
'0x1A' int(s) ValueError
'1_000' int(s) 1000
'1__000' int(s) ValueError
'١٢' int(s) 12
'0x1A' int(s, 0) 26
The C library's Parsing a number from text ↗ is the same question asked of atoi and strtol, where the lenient answer is also the short one.