2009-11-03 24 views
13

Python adora elevar excepciones, lo que generalmente es genial. Pero estoy enfrentando algunas cadenas que desesperadamente quiero convertir a enteros usando la semántica atoi/atof de C, por ej. atoi de "3 de 12", "3/12", "3/12", todos deben convertirse en 3; atof ("3.14 segundos") debería convertirse en 3.14; atoi ("puntuación de -99") 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 de int y float de Python.Python equivalente a atoi/atof

Lo mejor que tengo hasta ahora, que es muy feo y difícil de extender a los diferentes formatos de flotador 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 

Respuesta

3

Es bastante sencillo de 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 
+1

' atoi ("¿Qué pasa con 2") 'debe devolver' 0', ya que comienza con 'w 'y' w' no es un dígito. – Quuxplusone

6

quizás al uso de una expresión regular rápido para tomar sólo la primera parte de la cadena que puede ser considerado numérico? Algo así como ...

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

de los flotadores y de enteros simplemente,

-?[0-9]+ 
+3

flotadores pueden tener '' e' o E' en ellos también –

7

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") 
+0

1 por lo que supongo es el algoritmo más simple que voy a ver aquí! –

+2

Simple, quizás, pero no realmente eficiente (especialmente si la parte textual de la cadena es larga en comparación con la parte numérica). – Amber

+0

Si la cadena puede tener un montón de basura, deberá usar un bucle en lugar de la recursión. Si está haciendo muchas conversiones, hay formas más rápidas de hacerlo. –

36

Si estás tan interesado para obtener exactamente la funcionalidad de c's atoi, ¿por qué no usarlo 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., idéntico código debería funcionar excepto que verá un camino diferente si se examina whereislib (sólo en muy, muy peculiares instalaciones deben fallar alguna vez este código para encontrar la biblioteca C runtime).

Si está interesado en evitar el uso directo de la biblioteca C, creo que podría obtener el prefijo correspondiente, p. con un RE como r'\s*([+-]?\d+)', y prueba int en eso.

+2

+1 ¡Gran respuesta! –

+0

Supongo que el mayor argumento en contra de esto es la dependencia de la plataforma (sin mencionar que las bibliotecas podrían teóricamente residir en diferentes ubicaciones incluso en la misma plataforma). – Amber

+1

@Andrew, tx! @Dav, sí, tienes que encontrar la DLL de libc (puede tener diferentes nombres y rutas), pero ayuda 'ctypes.util.find_library' - Acabo de editar la respuesta para mostrar cómo usarla. –

0

creo que lo hará la charla 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 
+1

Eso no funciona correctamente para '314e-2' –

-1

¿Qué tal esto?

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

aquí q = '' inicialmente. s es la cadena de entrada num es la respuesta final. – abhilash