1976 Stimmen

Wie prüfe ich, ob eine Zeichenkette eine Zahl (Float) ist?

Wie kann man am besten prüfen, ob eine Zeichenkette in Python als Zahl dargestellt werden kann?

Die Funktion, die ich derzeit habe, ist:

def is_number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

Das ist nicht nur hässlich und langsam, sondern wirkt auch klobig. Ich habe jedoch keine bessere Methode gefunden, da der Aufruf von float() in der Hauptfunktion ist noch schlimmer.

101 Stimmen

Was ist falsch an Ihrer derzeitigen Lösung? Sie ist kurz, schnell und lesbar.

7 Stimmen

Und Sie müssen nicht nur True oder False zurückgeben. Sie können stattdessen den Wert entsprechend modifiziert zurückgeben - zum Beispiel könnten Sie dies verwenden, um Nicht-Zahlen in Anführungszeichen zu setzen.

10 Stimmen

Wäre es nicht besser, im Falle einer erfolgreichen Konvertierung das Ergebnis von float(s) zurückzugeben? Sie haben immer noch die Erfolgsprüfung (Ergebnis ist False) und Sie haben die Umwandlung, die Sie wahrscheinlich sowieso wollen.

1781voto

Zoomulator Punkte 20148

Falls Sie (positive, vorzeichenlose) Ganzzahlen anstelle von Fließkommazahlen analysieren möchten, können Sie die isdigit() Funktion für String-Objekte.

>>> a = "03523"
>>> a.isdigit()
True
>>> b = "963spam"
>>> b.isdigit()
False

String-Methoden - isdigit() : Python2 , Python3

Es gibt auch etwas über Unicode-Strings, mit denen ich nicht allzu vertraut bin Unicode - Ist dezimal/dezimal

255 Stimmen

Das ist auch bei den Negativen so.

35 Stimmen

Schlägt auch bei Exponentialen fehl: '1e3'.isdigit() --> False

40 Stimmen

Während Number != Digit, Leute, die nach Möglichkeiten suchen, um zu testen, ob eine Zeichenkette eine ganze Zahl enthält, können sehr gut über diese Frage stolpern, und die isDigit Ansatz kann sehr gut für ihre Anwendung geeignet sein.

780voto

S.Lott Punkte 371691

Was nicht nur hässlich und langsam ist

Ich würde beides bestreiten.

Eine Regex- oder andere String-Parsing-Methode wäre hässlicher und langsamer.

Ich bin mir nicht sicher, ob irgendetwas schneller sein könnte als die obige Methode. Es ruft die Funktion auf und kehrt zurück. Try/Catch verursacht keinen großen Overhead, da die häufigsten Ausnahmen ohne eine umfangreiche Suche in den Stack-Frames abgefangen werden.

Das Problem ist, dass jede numerische Konvertierungsfunktion zwei Arten von Ergebnissen hat

  • Eine Zahl, wenn die Zahl gültig ist
  • Ein Statuscode (z. B. über errno) oder eine Ausnahme, die anzeigt, dass keine gültige Zahl geparst werden konnte.

C (als Beispiel) umgeht dieses Problem auf verschiedene Weise. Python legt es klar und deutlich dar.

Ich denke, dass Ihr Code für diese Aufgabe perfekt ist.

31 Stimmen

Ich glaube nicht, dass der Code perfekt ist (aber ich denke, er ist sehr nahe dran): es ist üblicher, die sólo das "geprüfte" Teil in der try Klausel, also würde ich die return True in einem else Klausel des try . Einer der Gründe dafür ist, dass ich bei dem Code in der Frage, wenn ich ihn überprüfen müsste, prüfen müsste, ob die zweite Anweisung in der try Klausel kann keinen ValueError auslösen: Zugegeben, das erfordert nicht allzu viel Zeit oder Gehirnschmalz, aber warum etwas verwenden, wenn keines benötigt wird?

5 Stimmen

Die Antwort scheint überzeugend zu sein, aber ich frage mich, warum sie nicht gleich mitgeliefert wird... Ich werde dies kopieren und auf jeden Fall verwenden.

17 Stimmen

Wie furchtbar. Wie wäre es, wenn es mir egal wäre, wie die Nummer lautet. es nur, dass es sich um eine Zahl handelt (was mich hierher gebracht hat)? Anstelle einer 1-zeiligen IsNumeric() Ich ende entweder mit einem Try/Catch oder einem anderen Wrapping ein Try/Catch. Ugh

333voto

TL;DR Die beste Lösung ist s.replace('.','',1).isdigit()

Ich habe einige Benchmarks Vergleich der verschiedenen Ansätze

def is_number_tryexcept(s):
    """ Returns True is string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False

import re    
def is_number_regex(s):
    """ Returns True is string is a number. """
    if re.match("^\d+?\.\d+?$", s) is None:
        return s.isdigit()
    return True

def is_number_repl_isdigit(s):
    """ Returns True is string is a number. """
    return s.replace('.','',1).isdigit()

Wenn die Zeichenkette keine Zahl ist, ist der except-Block recht langsam. Noch wichtiger ist jedoch, dass die try-except-Methode der einzige Ansatz ist, der wissenschaftliche Notationen korrekt behandelt.

funcs = [
          is_number_tryexcept, 
          is_number_regex,
          is_number_repl_isdigit
          ]

a_float = '.1234'

print('Float notation ".1234" is not supported by:')
for f in funcs:
    if not f(a_float):
        print('\t -', f.__name__)

Die Float-Notation ".1234" wird nicht unterstützt von:
- is_number_regex

scientific1 = '1.000000e+50'
scientific2 = '1e50'

print('Scientific notation "1.000000e+50" is not supported by:')
for f in funcs:
    if not f(scientific1):
        print('\t -', f.__name__)

print('Scientific notation "1e50" is not supported by:')
for f in funcs:
    if not f(scientific2):
        print('\t -', f.__name__)

Die wissenschaftliche Notation "1.000000e+50" wird nicht unterstützt von:
- is_number_regex
- is_number_repl_isdigit
Die wissenschaftliche Notation "1e50" wird nicht unterstützt von:
- is_number_regex
- is_number_repl_isdigit

EDIT: Die Benchmark-Ergebnisse

import timeit

test_cases = ['1.12345', '1.12.345', 'abc12345', '12345']
times_n = {f.__name__:[] for f in funcs}

for t in test_cases:
    for f in funcs:
        f = f.__name__
        times_n[f].append(min(timeit.Timer('%s(t)' %f, 
                      'from __main__ import %s, t' %f)
                              .repeat(repeat=3, number=1000000)))

wobei die folgenden Funktionen getestet wurden

from re import match as re_match
from re import compile as re_compile

def is_number_tryexcept(s):
    """ Returns True is string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False

def is_number_regex(s):
    """ Returns True is string is a number. """
    if re_match("^\d+?\.\d+?$", s) is None:
        return s.isdigit()
    return True

comp = re_compile("^\d+?\.\d+?$")    

def compiled_regex(s):
    """ Returns True is string is a number. """
    if comp.match(s) is None:
        return s.isdigit()
    return True

def is_number_repl_isdigit(s):
    """ Returns True is string is a number. """
    return s.replace('.','',1).isdigit()

enter image description here

33 Stimmen

Für schöne Diagramme +1. Ich sah Benchmark und sah Grafik, alle TL;DR Sache wurde klar und intuitiv.

0 Stimmen

Ich stimme @JCChuks zu: Das Diagramm ist sehr hilfreich, um alle TL;DR schnell zu erfassen. Aber ich denke, ein TL;DR (wie : TL;DR : Die beste Lösung ist s.replace('.','',1).isdigit() ) sollte am Anfang dieser Antwort erscheinen. Auf jeden Fall sollte es die akzeptierte sein. Danke!

0 Stimmen

Der TLDR ist irreführend und unaufrichtig. Am besten" zu sein, hat nichts mit einem Leistungsmaßstab zu tun. Ich zum Beispiel lege in der Regel viel mehr Wert auf die Lesbarkeit als auf Mikrooptimierungen, so dass Benchmarks bei der Bestimmung der besten Lösung für meinen Kontext fast keine Rolle spielen. TLDR wäre genauer, wenn es hieße: "die beste Lösung, wenn sie nach der Ausführungszeit aus einer kleinen Gruppe beliebiger Benchmarks geordnet ist".

83voto

W7GVR Punkte 1865

Es gibt eine Ausnahme, die Sie vielleicht berücksichtigen sollten: die Zeichenkette 'NaN'

Wenn Sie wollen, dass is_number für 'NaN' FALSE zurückgibt, wird dieser Code nicht funktionieren, da Python ihn in seine Darstellung einer Zahl umwandelt, die keine Zahl ist (es geht um Identitätsprobleme):

>>> float('NaN')
nan

Ansonsten sollte ich mich eigentlich für das Stück Code bedanken, das ich jetzt ausgiebig benutze :)

G.

4 Stimmen

Eigentlich, NaN könnte ein guter Rückgabewert sein (anstelle von False ), wenn der übergebene Text nicht tatsächlich eine Zahl darstellt. Die Prüfung darauf ist ziemlich mühsam (Pythons float Typ wirklich eine Methode dafür braucht), aber man kann sie in Berechnungen verwenden, ohne dass ein Fehler auftritt, und muss nur das Ergebnis überprüfen.

11 Stimmen

Eine weitere Ausnahme ist die Zeichenkette 'inf' . Entweder inf o NaN kann auch mit einem vorangestellten + o - und trotzdem akzeptiert werden.

4 Stimmen

Wenn Sie für NaN und Inf False zurückgeben wollen, ändern Sie die Zeile in x = float(s); return (x == x) und (x - 1 != x). Dies sollte für alle Fließkommazahlen außer Inf und NaN True zurückgeben.

67voto

haxwithaxe Punkte 795

Wie wäre es damit:

'3.14'.replace('.','',1).isdigit()

die nur dann true zurückgibt, wenn in der Ziffernfolge ein oder kein '.' enthalten ist.

'3.14.5'.replace('.','',1).isdigit()

wird false zurückgegeben

edit: habe gerade einen anderen Kommentar gesehen ... Hinzufügen einer .replace(badstuff,'',maxnum_badstuff) für andere Fälle getan werden kann. wenn Sie Salz und nicht beliebige Gewürze (ref: xkcd#974 ) das reicht völlig aus :P

8 Stimmen

Dies gilt jedoch nicht für negative Zahlen.

6 Stimmen

Oder Zahlen mit Exponenten wie 1.234e56 (was auch geschrieben werden könnte als +1.234E+56 und einige weitere Varianten).

0 Stimmen

re.match(r'^[+-]*(0[xbo])?[0-9A-Fa-f]*\.?[0-9A-Fa-f]*(E[+-]*‌​[0-9A-Fa-f]+)$', 'str') sollten besser geeignet sein, eine Zahl zu ermitteln (aber nicht alle, das behaupte ich nicht). Ich empfehle nicht, diesen Code zu verwenden, sondern lieber den Originalcode des Fragestellers.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X