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.

10voto

Die Eingabe kann folgendermaßen aussehen:

a="50" b=50 c=50.1 d="50.1"


1 Allgemeine Eingabe:

Die Eingabe dieser Funktion kann alles sein!

Findet heraus, ob die angegebene Variable numerisch ist. Numerische Zeichenfolgen bestehen aus einem optionalen Vorzeichen, einer beliebigen Anzahl von Ziffern, einem optionalen Dezimalteil und einem optionalen Exponentialteil. Somit ist +0123.45e6 ein gültiger numerischer Wert. Hexadezimale (z. B. 0xf4c3b00c) und binäre (z. B. 0b10100111001) Schreibweisen sind nicht zulässig.

ist_numerisch Funktion

import ast
import numbers              
def is_numeric(obj):
    if isinstance(obj, numbers.Number):
        return True
    elif isinstance(obj, str):
        nodes = list(ast.walk(ast.parse(obj)))[1:]
        if not isinstance(nodes[0], ast.Expr):
            return False
        if not isinstance(nodes[-1], ast.Num):
            return False
        nodes = nodes[1:-1]
        for i in range(len(nodes)):
            #if used + or - in digit :
            if i % 2 == 0:
                if not isinstance(nodes[i], ast.UnaryOp):
                    return False
            else:
                if not isinstance(nodes[i], (ast.USub, ast.UAdd)):
                    return False
        return True
    else:
        return False

Test:

>>> is_numeric("54")
True
>>> is_numeric("54.545")
True
>>> is_numeric("0x45")
True

is_float Funktion

Findet heraus, ob die angegebene Variable ein Float ist. Float-Zeichenketten bestehen aus einem optionalen Vorzeichen, einer beliebigen Anzahl von Ziffern, ...

import ast

def is_float(obj):
    if isinstance(obj, float):
        return True
    if isinstance(obj, int):
        return False
    elif isinstance(obj, str):
        nodes = list(ast.walk(ast.parse(obj)))[1:]
        if not isinstance(nodes[0], ast.Expr):
            return False
        if not isinstance(nodes[-1], ast.Num):
            return False
        if not isinstance(nodes[-1].n, float):
            return False
        nodes = nodes[1:-1]
        for i in range(len(nodes)):
            if i % 2 == 0:
                if not isinstance(nodes[i], ast.UnaryOp):
                    return False
            else:
                if not isinstance(nodes[i], (ast.USub, ast.UAdd)):
                    return False
        return True
    else:
        return False

Test:

>>> is_float("5.4")
True
>>> is_float("5")
False
>>> is_float(5)
False
>>> is_float("5")
False
>>> is_float("+5.4")
True

Was ist ast ?


2- Wenn Sie sicher sind, dass der Inhalt der Variablen Zeichenfolge :

verwenden. str.isdigit() Methode

>>> a=454
>>> a.isdigit()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute 'isdigit'
>>> a="454"
>>> a.isdigit()
True

3 - Numerische Eingabe:

int-Wert erkennen:

>>> isinstance("54", int)
False
>>> isinstance(54, int)
True
>>> 

Schwimmer erkennen:

>>> isinstance("45.1", float)
False
>>> isinstance(45.1, float)
True

0 Stimmen

Was ist " ast "?

0 Stimmen

Eine Einblick zu den Typen

0 Stimmen

Dies scheitert bei einem Test von is_numeric("String 1") Wrapped die Methode in try/except und funktioniert.

10voto

Ron Reiter Punkte 3586

Ich wollte sehen, welche Methode am schnellsten ist. Insgesamt wurden die besten und beständigsten Ergebnisse mit der check_replace Funktion. Die schnellsten Ergebnisse lieferte die Funktion check_exception Funktion, aber nur, wenn keine Ausnahme ausgelöst wurde - das heißt, ihr Code ist der effizienteste, aber der Overhead durch das Auslösen einer Ausnahme ist ziemlich groß.

Bitte beachten Sie, dass die Überprüfung auf einen erfolgreichen Wurf die einzige Methode ist, die genau ist, zum Beispiel funktioniert dies mit check_exception aber die beiden anderen Testfunktionen geben False für einen gültigen Float zurück:

huge_number = float('1e+100')

Hier ist der Benchmark-Code:

import time, re, random, string

ITERATIONS = 10000000

class Timer:    
    def __enter__(self):
        self.start = time.clock()
        return self
    def __exit__(self, *args):
        self.end = time.clock()
        self.interval = self.end - self.start

def check_regexp(x):
    return re.compile("^\d*\.?\d*$").match(x) is not None

def check_replace(x):
    return x.replace('.','',1).isdigit()

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

to_check = [check_regexp, check_replace, check_exception]

print('preparing data...')
good_numbers = [
    str(random.random() / random.random()) 
    for x in range(ITERATIONS)]

bad_numbers = ['.' + x for x in good_numbers]

strings = [
    ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(random.randint(1,10)))
    for x in range(ITERATIONS)]

print('running test...')
for func in to_check:
    with Timer() as t:
        for x in good_numbers:
            res = func(x)
    print('%s with good floats: %s' % (func.__name__, t.interval))
    with Timer() as t:
        for x in bad_numbers:
            res = func(x)
    print('%s with bad floats: %s' % (func.__name__, t.interval))
    with Timer() as t:
        for x in strings:
            res = func(x)
    print('%s with strings: %s' % (func.__name__, t.interval))

Hier sind die Ergebnisse mit Python 2.7.10 auf einem 2017 MacBook Pro 13:

check_regexp with good floats: 12.688639
check_regexp with bad floats: 11.624862
check_regexp with strings: 11.349414
check_replace with good floats: 4.419841
check_replace with bad floats: 4.294909
check_replace with strings: 4.086358
check_exception with good floats: 3.276668
check_exception with bad floats: 13.843092
check_exception with strings: 15.786169

Hier sind die Ergebnisse mit Python 3.6.5 auf einem 2017 MacBook Pro 13:

check_regexp with good floats: 13.472906000000009
check_regexp with bad floats: 12.977665000000016
check_regexp with strings: 12.417542999999995
check_replace with good floats: 6.011045999999993
check_replace with bad floats: 4.849356
check_replace with strings: 4.282754000000011
check_exception with good floats: 6.039081999999979
check_exception with bad floats: 9.322753000000006
check_exception with strings: 9.952595000000002

Hier sind die Ergebnisse mit PyPy 2.7.13 auf einem 2017 MacBook Pro 13:

check_regexp with good floats: 2.693217
check_regexp with bad floats: 2.744819
check_regexp with strings: 2.532414
check_replace with good floats: 0.604367
check_replace with bad floats: 0.538169
check_replace with strings: 0.598664
check_exception with good floats: 1.944103
check_exception with bad floats: 2.449182
check_exception with strings: 2.200056

10 Stimmen

Sie sollten die Leistung auch für ungültige Fälle testen. Bei diesen Zahlen wird keine Ausnahme ausgelöst, was genau der "langsame" Teil ist.

1 Stimmen

@UgoMéda ich habe deinen Rat von 2013 befolgt und es getan :)

0 Stimmen

"Bitte beachten Sie, dass die Prüfung auf einen erfolgreichen Cast die einzige Methode ist, die genau ist" <- das ist nicht wirklich wahr. Ich habe Ihren Test mit dem regexp in meiner Antwort oben durchgeführt, und er läuft tatsächlich schneller als der regexp. Ich füge die Ergebnisse zu meiner Antwort oben hinzu.

9voto

a1an Punkte 3214

Um das Ganze zusammenzufassen und auf Nan, Unendlichkeit und komplexe Zahlen zu prüfen (es scheint, dass sie mit j und nicht mit i angegeben werden, d.h. 1+2j), ergibt sich folgendes:

def is_number(s):
    try:
        n=str(float(s))
        if n == "nan" or n=="inf" or n=="-inf" : return False
    except ValueError:
        try:
            complex(s) # for complex
        except ValueError:
            return False
    return True

0 Stimmen

Bislang die beste Antwort. Danke

7voto

zardosht Punkte 2458

str.isnumeric()

Rückkehr True wenn alle Zeichen in der Zeichenkette numerische Zeichen sind, und es mindestens ein Zeichen gibt, False anders. Numerische Zeichen umfassen Ziffern und alle Zeichen, die die Unicode-Eigenschaft numerischer Wert haben, z. B. U+2155, VULGAR FRACTION ONE FÜNFTE. Formal sind numerische Zeichen solche mit dem Eigenschaftswert Numeric_Type=Digit, Numeric_Type=Decimal oder Numeric_Type=Numeric.

str.isdecimal()

Rückkehr True wenn alle Zeichen in der Zeichenkette Dezimalzeichen sind und mindestens ein Zeichen vorhanden ist, False anders. Dezimale Zeichen sind Zeichen, die zur Bildung von Zahlen zur Basis 10 verwendet werden können, z. B. U+0660, ARABISCH-INDISCHE ZIFFER NULL. Formal ist ein Dezimalzeichen ein Zeichen in der allgemeinen Unicode-Kategorie "Nd".

Beide sind für String-Typen ab Python 3.0 verfügbar.

7voto

Ich denke, Ihre Lösung ist gut, aber es gibt ist eine korrekte Regexp-Implementierung.

Es scheint eine Menge regexp Hass gegenüber diesen Antworten, die ich denke, ist ungerechtfertigt, regexps kann ziemlich sauber und korrekt und schnell sein. Es hängt wirklich davon ab, was Sie versuchen, zu tun. Die ursprüngliche Frage war, wie man "prüfen kann, ob eine Zeichenkette als Zahl (float) dargestellt werden kann" (wie in Ihrem Titel). Vermutlich möchten Sie den Zahlen-/Fließkommawert verwenden, sobald Sie überprüft haben, dass er gültig ist. In diesem Fall macht Ihr try/except sehr viel Sinn. Aber wenn Sie aus irgendeinem Grund nur überprüfen wollen, dass ein String es un Nummer dann funktioniert ein Regex auch gut, aber es ist schwer, es richtig zu machen. Ich denke, die meisten der Regex-Antworten bisher, zum Beispiel, nicht richtig parsen Zeichenfolgen ohne einen Integer-Teil (wie ".7"), die ein Float ist, soweit Python betrifft. Und das ist etwas schwierig in einer einzelnen Regex zu überprüfen, in der der Bruchteil nicht erforderlich ist. Ich habe zwei Regex eingefügt, um dies zu zeigen.

Es stellt sich die interessante Frage, was eine "Zahl" ist. Schließen Sie "inf" ein, das in Python als Fließkommazahl gültig ist? Oder schließt man Zahlen ein, die zwar "Zahlen" sind, aber vielleicht nicht in Python dargestellt werden können (z. B. Zahlen, die größer sind als der float max).

Es gibt auch Unklarheiten bei der Analyse von Zahlen. Was ist zum Beispiel mit "--20"? Ist das eine "Zahl"? Ist dies eine legale Art, "20" darzustellen? In Python können Sie "var = --20" eingeben und den Wert auf 20 setzen (obwohl dies eigentlich nur möglich ist, weil es als Ausdruck behandelt wird), aber float("--20") funktioniert nicht.

Wie auch immer, ohne weitere Informationen, hier ist eine Regex, die ich glaube, deckt alle ints und Floats wie Python sie parst .

# Doesn't properly handle floats missing the integer part, such as ".7"
SIMPLE_FLOAT_REGEXP = re.compile(r'^[-+]?[0-9]+\.?[0-9]+([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56"      # sign (-)
                            #     integer (12)
                            #           mantissa (34)
                            #                    exponent (E+56)

# Should handle all floats
FLOAT_REGEXP = re.compile(r'^[-+]?([0-9]+|[0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56"      # sign (-)
                            #     integer (12)
                            #           OR
                            #             int/mantissa (12.34)
                            #                            exponent (E+56)

def is_float(str):
  return True if FLOAT_REGEXP.match(str) else False

Einige Beispiel-Testwerte:

True  <- +42
True  <- +42.42
False <- +42.42.22
True  <- +42.42e22
True  <- +42.42E-22
False <- +42.42e-22.8
True  <- .42
False <- 42nope

Die Ausführung des Benchmarking-Codes in @ron-reiter's Antwort zeigt, dass diese Regex tatsächlich schneller ist als die normale Regex und viel schneller mit schlechten Werten umgeht als die Ausnahme, was durchaus sinnvoll ist. Ergebnisse:

check_regexp with good floats: 18.001921
check_regexp with bad floats: 17.861423
check_regexp with strings: 17.558862
check_correct_regexp with good floats: 11.04428
check_correct_regexp with bad floats: 8.71211
check_correct_regexp with strings: 8.144161
check_replace with good floats: 6.020597
check_replace with bad floats: 5.343049
check_replace with strings: 5.091642
check_exception with good floats: 5.201605
check_exception with bad floats: 23.921864
check_exception with strings: 23.755481

0 Stimmen

Ich hoffe, das ist richtig - ich würde gerne von Gegenbeispielen hören :)

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