2009-11-03 17 views
13

Python liebt es, Ausnahmen zu machen, was normalerweise großartig ist. Aber ich bin mit einigen Strings konfrontiert, die ich verzweifelt in ganze Zahlen umwandeln möchte, indem ich Cs atoi/atof-Semantik verwende - z. atoi von "3 von 12", "3/12", "3/12", sollten alle 3 werden; atof ("3,14 Sekunden") sollte 3,14 werden; atoi ("-99 Punkte") sollte -99 werden. Python hat natürlich atoi- und atof-Funktionen, die sich nicht wie atoi und atof verhalten und genau wie Pythons eigene int- und float-Konstruktoren.Python entspricht atoi/atof

Das Beste, was ich bisher habe, die wirklich hässlich und schwer ist verfügbar für die verschiedenen Schwimmer Formate zu erweitern:

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 

Antwort

3

Es ist ziemlich einfach, dies zu tun mit regulären Ausdrücken:

>>> 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 ("was ist mit 2") 'sollte' 0' zurückgeben, da es mit einem 'w beginnt 'und' w' ist keine Ziffer. – Quuxplusone

6

Vielleicht eine schnelle Regex verwenden nur den ersten Teil der Zeichenfolge zu greifen, die sein kann als numerisch betrachtet? So etwas wie ...

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

für Schwimmer und für ints nur,

-?[0-9]+ 
+3

Schwimmer 'kann e' oder' e' in ihnen auch –

7

Ich denke, die iterative Version ist besser als die rekursive 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") 
+0

+1 für das, was ich vermute, ist der einfachste Algorithmus werde ich hier sehen! –

+2

Einfach, vielleicht, aber nicht wirklich effizient (besonders wenn der Textteil der Zeichenkette im Vergleich zum numerischen Teil lang ist). – Amber

+0

Wenn die Zeichenfolge viel Junk-Material enthalten kann, müssen Sie eine Schleife anstelle der Rekursion verwenden. Wenn Sie viele Conversions durchführen, gibt es schnellere Möglichkeiten. –

36

Wenn Sie so scharf sind auf genau die Funktionalität von c atoi bekommen, warum nicht direkt verwenden? ZB auf meinem 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., sollte identisch Code außer arbeiten, dass Sie einen anderen Weg sehen werden, wenn Sie untersuchen whereislib (nur auf wirklich, wirklich eigenartige Installationen sollten immer dieser Code fehlschlagen um die C-Laufzeitbibliothek zu finden.

Wenn Sie direkte C-Bibliothek verwenden möchten, können Sie das entsprechende Präfix, z. mit einem RE wie r'\s*([+-]?\d+)', und versuchen Sie int darauf.

+2

+1 Große Antwort! –

+0

Meine Vermutung wäre das größte Argument dagegen ist die Plattformabhängigkeit (nicht zu erwähnen, dass Bibliotheken theoretisch an verschiedenen Orten auch auf der gleichen Plattform residieren könnten). – Amber

+1

@Andrew, TX! @Dav, ja, du musst die libc-DLL finden (sie kann verschiedene Namen und Pfade haben), aber 'ctypes.util.find_library' hilft - ich habe gerade die Antwort bearbeitet, um zu zeigen, wie man sie benutzt. –

0

Ich denke, ich werde es durch char char tun:

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

Das funktioniert nicht richtig für '314e-2' –

-1

Wie wäre das?

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

hier q = '' zunächst. s ist die Eingabezeichenfolge num ist die endgültige Antwort. – abhilash