2009-11-03 7 views
13

Python adore lever des exceptions, ce qui est généralement génial. Mais je suis confronté à des chaînes que je veux désespérément convertir en entiers en utilisant la sémantique atoi/atof de C - par ex. atoi de "3 sur 12", "3/12", "3/12", devraient tous devenir 3; atof ("3,14 secondes") devrait devenir 3.14; atoi ("-99 score") devrait devenir -99. Python a bien sûr des fonctions atoi et atof, qui ne se comportent pas comme atoi et atof et exactement comme les propres constructeurs int et float de Python.Python équivalent à atoi/atof

Le meilleur que j'ai à ce jour, ce qui est vraiment laid et difficile à étendre aux différents formats float 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 

Répondre

3

Il est assez simple de le faire avec des expressions régulières:

>>> 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 ("what about 2") 'devrait retourner' 0', car il commence par un 'w 'et' w' n'est pas un chiffre. – Quuxplusone

6

peut-être utiliser un regex rapide pour saisir que la première partie de la chaîne qui peut être considéré comme numérique? Quelque chose comme ...

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

pour les flotteurs et ints juste,

-?[0-9]+ 
+3

flotteurs peuvent avoir 'e' ou' e' en eux aussi –

7

Je pense que la version itérative est meilleure que la version récursive

# 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 pour ce que je devine est l'algorithme le plus simple que je vais voir ici! –

+2

Simple, peut-être, mais pas vraiment efficace (surtout si la partie textuelle de la chaîne est longue par rapport à la partie numérique). – Amber

+0

Si la chaîne peut contenir beaucoup de courrier indésirable, vous devrez utiliser une boucle au lieu de la récursivité. Si vous faites beaucoup de conversions, il existe des moyens plus rapides de le faire. –

36

Si vous êtes tellement envie pour obtenir exactement la fonctionnalité de atoi c, pourquoi ne pas l'utiliser directement? Par exemple, sur mon 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 

Dans Linux, Windows, etc, code identique devrait fonctionner sauf que vous verrez un autre chemin si vous examinez whereislib (uniquement sur vraiment, vraiment des installations particulières de ce code devrait jamais échouer pour trouver la bibliothèque d'exécution C).

Si vous souhaitez éviter l'utilisation directe de la bibliothèque C, je suppose que vous pouvez saisir le préfixe approprié, par ex. avec un RE tel que r'\s*([+-]?\d+)', et essayez int sur cela.

+2

+1 Bonne réponse! –

+0

Ma conjecture serait le plus grand argument contre cela est la dépendance de la plate-forme (sans compter que les bibliothèques pourraient théoriquement résider dans des endroits différents, même sur la même plate-forme). – Amber

+1

@Andrew, tx! @Dav, oui, vous devez localiser la DLL de libc (elle peut avoir des noms et des chemins différents), mais 'ctypes.util.find_library' aide - je viens d'éditer la réponse pour montrer comment l'utiliser. –

0

Je pense que je vais le faire carboniser par 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

Cela ne fonctionne pas correctement pour '314e-2' –

-1

Comment cela?

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

ici q = '' initialement. S est la chaîne d'entrée num est la réponse finale. – abhilash