Python's equivalent of C's atoi
python - LeetCode 8: String to Integer (atoi) - Code Review Stack Exchange
Python equivalent to atoi / atof - Stack Overflow
python - Convert String to Int without int() - Stack Overflow
I would like to convert the beginning of a string to an int like C's function atoi does. E.g., called with the string '21st century' it should return 21. Is there such a function in Python?
Nice solution, it's compact and simple to understand. There is little to improve, few suggestions:
- Imports: there are a lot of imports, maybe leftovers of previous attempts.
- Try Except block: should be around the code that can cause the exception.
Applying the suggestions:
import re
class Solution:
def myAtoi(self, s: str) -> int:
MAX, MIN = 2147483647, -2147483648
s = re.findall(r'^\s*[+-]?\d+', s)
try:
res = int(''.join(s))
except:
return 0
if res > MAX:
return MAX
if res < MIN:
return MIN
return res
Performance
Runtime: 36 ms, faster than 51.56% of Python3 online submissions
Memory Usage: 14.1 MB, less than 32.27% of Python3 online submissions
Regex makes the code compact but is not the fastest approach. Faster solutions iterate through the string character by character.
Magic numbers
2147483647, -2147483648
are actually
1<<31 - 1, -(1<<31)
which better conveys your intent: the limits of a signed 32-bit integer.
Pre-compile your regex
Consider putting a
DIGIT_PATTERN = re.compile(r'^\s*[+-]?\d+')
in global scope, so that multiple calls to myAtoi are faster.
Never bare except
You should likely instead except ValueError, which is more narrow and well-defined.
If you're so keen on getting exactly the functionality of c's atoi, why not use it directly? E.g., on my Mac,
>>> import ctypes, ctypes.util
>>> whereislib = ctypes.util.find_library('c')
>>> whereislib
'/usr/lib/libc.dylib'
>>> clib = ctypes.cdll.LoadLibrary(whereislib)
>>> clib.atoi('-99foobar')
-99
In Linux, Windows, etc, identical code should work except that you'll see a different path if you examine whereislib (only on really, really peculiar installations should this code ever fail to find the C runtime library).
If you're keen on avoiding direct C library usage, I guess you could grab the relevant prefix, e.g. with a RE such as r'\s*([+-]?\d+)', and try int on that.
I think the iterative version is better than the recursive version
# Iterative
def atof(s):
s,_,_=s.partition(' ') # eg. this helps by trimming off at the first space
while s:
try:
return float(s)
except:
s=s[:-1]
return 0.0
# Recursive
def atof(s):
try:
return float(s)
except:
if not s:
return 0.0
return atof(s[:-1])
print atof("3 of 12")
print atof("3/12")
print atof("3 / 12")
print atof("3.14 seconds")
print atof("314e-2 seconds")
print atof("-99 score")
print atof("hello world")
Refer to a basic atoi in C:
int myAtoi(char *str)
{
int res = 0; // Initialize result
// Iterate through all characters of input string and update result
for (int i = 0; str[i] != '\0'; ++i)
res = res*10 + str[i] - '0';
// return result.
return res;
}
Which translates into the Python:
def atoi(s):
rtr=0
for c in s:
rtr=rtr*10 + ord(c) - ord('0')
return rtr
Test it:
>>> atoi('123456789')
123456789
If you want to accommodate an optional sign and whitespace the way that int does:
def atoi(s):
rtr, sign=0, 1
s=s.strip()
if s[0] in '+-':
sc, s=s[0], s[1:]
if sc=='-':
sign=-1
for c in s:
rtr=rtr*10 + ord(c) - ord('0')
return sign*rtr
Now add exceptions and you are there!
This is really inefficient but:
>>> zero = ord("0")
>>> s = "1234"
>>> sum([x * 10**i for i, x in enumerate(map(lambda x: x - zero, map(ord, s))[::-1])])
1234
This is slightly better:
>>>> sum([x * 10**i for i, x in enumerate([ord(x) - zero for x in s[::-1]])])
1234
>>> atoi = lambda s: sum([x * 10**i for i, x in enumerate([ord(x) - zero for x in s[::-1]])])
>>> atoi("1234")
1234