Python equivalente a atoi / atof

A Python le encanta levantar excepciones, lo que suele ser genial. Pero me enfrento a algunas cadenas que quiero convertir desesperadamente a números enteros usando la semántica atoi / atof de C: por ejemplo, atoi de “3 de 12”, “3/12”, “3/12”, debería convertirse en 3; atof (“3.14 segundos”) debe convertirse en 3.14; atoi (“-99 score”) debería convertirse en -99. Python, por supuesto, tiene funciones atoi y atof, que no se comportan como atoi y atof, y exactamente como los propios constructores int y float de Python.

Lo mejor que tengo hasta ahora, lo que es realmente feo y difícil de extender a los diversos formatos flotantes disponibles:

value = 1 s = str(s).strip() if s.startswith("-"): value = -1 s = s[1:] elif s.startswith("+"): s = s[1:] try: mul = int("".join(itertools.takewhile(str.isdigit, s))) except (TypeError, ValueError, AttributeError): mul = 0 return mul * value 

Es bastante sencillo hacer esto con expresiones regulares:

 >>> import re >>> p = re.compile(r'[^\d-]*(-?[\d]+(\.[\d]*)?([eE][+-]?[\d]+)?)') >>> def test(seq): for s in seq: m = p.match(s) if m: result = m.groups()[0] if "." in result or "e" in result or "E" in result: print "{0} -> {1}".format(s, float(result)) else: print '"{0}" -> {1}'.format(s, int(result)) else: print s, "no match" >>> test(s) "1 0" -> 1 "3 of 12" -> 3 "3 1/2" -> 3 "3/12" -> 3 3.15 seconds -> 3.15 3.0E+102 -> 3e+102 "what about 2?" -> 2 "what about -2?" -> -2 2.10a -> 2.1 

Si está tan interesado en obtener exactamente la funcionalidad de c’s atoi , ¿por qué no usarla directamente? Por ejemplo, en mi 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 

En Linux, Windows, etc., el código idéntico debería funcionar, excepto que verá una ruta diferente si examina whereislib (solo en instalaciones realmente peculiares si este código no encuentra la biblioteca de tiempo de ejecución de C).

Si está interesado en evitar el uso directo de la biblioteca de C, supongo que podría tomar el prefijo relevante, por ejemplo, con un RE como r'\s*([+-]?\d+)' , e intente con eso.

Creo que la versión iterativa es mejor que la versión recursiva.

 # 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") 

¿Quizás use una expresión regular rápida para tomar solo la primera parte de la cadena que puede considerarse numérica? Algo como…

 -?[0-9]+(?:\.[0-9]+)? 

para flotadores y para ints solo,

 -?[0-9]+ 

Creo que lo haré char por char:

 def myatof(s): try: return float(s); except: last_result = None for i in range(1, len(s)): try: last_result = float(s[:i]) except: return last_result return last_result 

¿Qué tal esto?

 num=int(q.join(re.findall(r'[\d-]',s)))