webentwicklung-frage-antwort-db.com.de

Wie kann ich überprüfen, ob eine Zeichenfolge ein int darstellt, ohne try/except zu verwenden?

Gibt es eine Möglichkeit festzustellen, ob eine Zeichenfolge eine Ganzzahl darstellt (z. B. '3', '-17', aber nicht '3.14' oder 'asfasfas'), ohne einen try/except-Mechanismus zu verwenden?

is_int('3.14') = False
is_int('-7')   = True
336
Adam Matan

Wenn Sie wirklich ärgerlich sind, try/excepts überall zu verwenden, schreiben Sie einfach eine Hilfsfunktion:

def RepresentsInt(s):
    try: 
        int(s)
        return True
    except ValueError:
        return False

>>> print RepresentsInt("+123")
True
>>> print RepresentsInt("10.0")
False

Es wird WEIT mehr Code sein, um alle Zeichenketten abzudecken, die Python für ganze Zahlen hält. Ich sage einfach Pythonic zu diesem Thema.

316
Triptych

mit positiven ganzen Zahlen können Sie .isdigit verwenden:

>>> '16'.isdigit()
True

es funktioniert jedoch nicht mit negativen ganzen Zahlen. Angenommen, Sie könnten Folgendes versuchen:

>>> s = '-17'
>>> s.startswith('-') and s[1:].isdigit()
True

es funktioniert nicht mit dem '16.0'-Format, was in diesem Sinne dem int-Casting ähnelt.

edit:

def check_int(s):
    if s[0] in ('-', '+'):
        return s[1:].isdigit()
    return s.isdigit()
567
SilentGhost

Wissen Sie, ich habe herausgefunden (und dies immer wieder getestet), dass Try/Exceptions aus irgendeinem Grund nicht so gut funktioniert. Ich versuche häufig verschiedene Wege, Dinge zu tun, und ich glaube nicht, dass ich jemals eine Methode gefunden habe, bei der try/außer verwendet wird, um das Beste der getesteten zu erreichen. Tatsächlich scheint es mir, dass diese Methoden in der Nähe von am schlimmsten, wenn nicht das Schlimmste. Nicht in jedem Fall, aber in vielen Fällen. Ich weiß, dass viele Leute sagen, es sei die "Pythonic" -Methode, aber das ist ein Bereich, in dem ich mich von ihnen trenne. Für mich ist es weder sehr performant noch sehr elegant, daher neige ich dazu, es nur zur Fehlerbehebung und Berichterstellung zu verwenden.

Ich würde mich bemängeln, dass PHP, Perl, Ruby, C und sogar die ausgeflippte Shell einfache Funktionen zum Testen eines Strings auf Ganzzahl besitzen, aber die sorgfältige Überprüfung dieser Annahmen hat mich aufgerüttelt! Anscheinend ist dieser Mangel eine übliche Krankheit. 

Hier ist eine schnelle und schmutzige Bearbeitung von Brunos Post:

import sys, time, re

g_intRegex = re.compile(r"^([+-]?[1-9]\d*|0)$")

testvals = [
    # integers
    0, 1, -1, 1.0, -1.0,
    '0', '0.','0.0', '1', '-1', '+1', '1.0', '-1.0', '+1.0', '06',
    # non-integers
    'abc 123',
    1.1, -1.1, '1.1', '-1.1', '+1.1',
    '1.1.1', '1.1.0', '1.0.1', '1.0.0',
    '1.0.', '1..0', '1..',
    '0.0.', '0..0', '0..',
    'one', object(), (1,2,3), [1,2,3], {'one':'two'},
    # with spaces
    ' 0 ', ' 0.', ' .0','.01 '
]

def isInt_try(v):
    try:     i = int(v)
    except:  return False
    return True

def isInt_str(v):
    v = str(v).strip()
    return v=='0' or (v if v.find('..') > -1 else v.lstrip('-+').rstrip('0').rstrip('.')).isdigit()

def isInt_re(v):
    import re
    if not hasattr(isInt_re, 'intRegex'):
        isInt_re.intRegex = re.compile(r"^([+-]?[1-9]\d*|0)$")
    return isInt_re.intRegex.match(str(v).strip()) is not None

def isInt_re2(v):
    return g_intRegex.match(str(v).strip()) is not None

def check_int(s):
    s = str(s)
    if s[0] in ('-', '+'):
        return s[1:].isdigit()
    return s.isdigit()    


def timeFunc(func, times):
    t1 = time.time()
    for n in range(times):
        for v in testvals: 
            r = func(v)
    t2 = time.time()
    return t2 - t1

def testFuncs(funcs):
    for func in funcs:
        sys.stdout.write( "\t%s\t|" % func.__name__)
    print()
    for v in testvals:
        if type(v) == type(''):
            sys.stdout.write("'%s'" % v)
        else:
            sys.stdout.write("%s" % str(v))
        for func in funcs:
            sys.stdout.write( "\t\t%s\t|" % func(v))
        sys.stdout.write("\r\n") 

if __== '__main__':
    print()
    print("tests..")
    testFuncs((isInt_try, isInt_str, isInt_re, isInt_re2, check_int))
    print()

    print("timings..")
    print("isInt_try:   %6.4f" % timeFunc(isInt_try, 10000))
    print("isInt_str:   %6.4f" % timeFunc(isInt_str, 10000)) 
    print("isInt_re:    %6.4f" % timeFunc(isInt_re, 10000))
    print("isInt_re2:   %6.4f" % timeFunc(isInt_re2, 10000))
    print("check_int:   %6.4f" % timeFunc(check_int, 10000))

Hier sind die Leistungsvergleichsergebnisse:

timings..
isInt_try:   0.6426
isInt_str:   0.7382
isInt_re:    1.1156
isInt_re2:   0.5344
check_int:   0.3452

Eine C-Methode könnte es einmal scannen und fertig sein. Eine C-Methode, die den String einmal durchläuft, wäre das Richtige, denke ich. 

BEARBEITEN: 

Ich habe den obigen Code überarbeitet, um in Python 3.5 zu funktionieren, und um die Funktion check_int aus der aktuell am meisten bewerteten Antwort aufzunehmen und um den aktuell beliebtesten Regex zu verwenden, den ich zum Testen von Integer-Hood finden kann. Dieser reguläre Ausdruck weist Zeichenfolgen wie 'abc 123' zurück. Ich habe 'abc 123' als Testwert hinzugefügt.

Es ist sehr interessant für mich an dieser Stelle anzumerken, dass KEINE der getesteten Funktionen, einschließlich der try-Methode, der populären check_int-Funktion und des beliebtesten regulären Ausdrucks für das Testen auf Ganzzahl, die richtigen Antworten für alle zurückgibt Testwerte (je nachdem, was Sie für die richtigen Antworten halten; siehe Testergebnisse unten).

Die integrierte Funktion int () schneidet den gebrochenen Teil einer Gleitkommazahl stillschweigend ab und gibt den ganzzahligen Teil vor dem Dezimalzeichen zurück, es sei denn, die Gleitkommazahl wird zuerst in eine Zeichenfolge konvertiert.

Die Funktion check_int () gibt für Werte wie 0,0 und 1,0 (die technisch ganze Zahlen sind) false zurück und für Werte wie '06' true.

Hier sind die aktuellen Testergebnisse (Python 3.5):

                  isInt_try |       isInt_str       |       isInt_re        |       isInt_re2       |   check_int   |
    0               True    |               True    |               True    |               True    |       True    |
    1               True    |               True    |               True    |               True    |       True    |
    -1              True    |               True    |               True    |               True    |       True    |
    1.0             True    |               True    |               False   |               False   |       False   |
    -1.0            True    |               True    |               False   |               False   |       False   |
    '0'             True    |               True    |               True    |               True    |       True    |
    '0.'            False   |               True    |               False   |               False   |       False   |
    '0.0'           False   |               True    |               False   |               False   |       False   |
    '1'             True    |               True    |               True    |               True    |       True    |
    '-1'            True    |               True    |               True    |               True    |       True    |
    '+1'            True    |               True    |               True    |               True    |       True    |
    '1.0'           False   |               True    |               False   |               False   |       False   |
    '-1.0'          False   |               True    |               False   |               False   |       False   |
    '+1.0'          False   |               True    |               False   |               False   |       False   |
    '06'            True    |               True    |               False   |               False   |       True    |
    'abc 123'       False   |               False   |               False   |               False   |       False   |
    1.1             True    |               False   |               False   |               False   |       False   |
    -1.1            True    |               False   |               False   |               False   |       False   |
    '1.1'           False   |               False   |               False   |               False   |       False   |
    '-1.1'          False   |               False   |               False   |               False   |       False   |
    '+1.1'          False   |               False   |               False   |               False   |       False   |
    '1.1.1'         False   |               False   |               False   |               False   |       False   |
    '1.1.0'         False   |               False   |               False   |               False   |       False   |
    '1.0.1'         False   |               False   |               False   |               False   |       False   |
    '1.0.0'         False   |               False   |               False   |               False   |       False   |
    '1.0.'          False   |               False   |               False   |               False   |       False   |
    '1..0'          False   |               False   |               False   |               False   |       False   |
    '1..'           False   |               False   |               False   |               False   |       False   |
    '0.0.'          False   |               False   |               False   |               False   |       False   |
    '0..0'          False   |               False   |               False   |               False   |       False   |
    '0..'           False   |               False   |               False   |               False   |       False   |
    'one'           False   |               False   |               False   |               False   |       False   |
    <obj..>         False   |               False   |               False   |               False   |       False   |
    (1, 2, 3)       False   |               False   |               False   |               False   |       False   |
    [1, 2, 3]       False   |               False   |               False   |               False   |       False   |
    {'one': 'two'}  False   |               False   |               False   |               False   |       False   |
    ' 0 '           True    |               True    |               True    |               True    |       False   |
    ' 0.'           False   |               True    |               False   |               False   |       False   |
    ' .0'           False   |               False   |               False   |               False   |       False   |
    '.01 '          False   |               False   |               False   |               False   |       False   |

Ich habe gerade versucht, diese Funktion hinzuzufügen:

def isInt_float(s):
    try:
        return float(str(s)).is_integer()
    except:
        return False

Es funktioniert fast genauso gut wie check_int (0,3486) und gibt true für Werte wie 1.0 und 0.0 und +1.0 und 0. und .0 usw. zurück. Aber auch für '06' gibt es wahr zurück. Nimm dein Gift, denke ich.

69
Shavais

Verwenden Sie einen regulären Ausdruck:

import re
def RepresentsInt(s):
    return re.match(r"[-+]?\d+$", s) is not None

Wenn Sie auch Dezimalbrüche akzeptieren müssen:

def RepresentsInt(s):
    return re.match(r"[-+]?\d+(\.0*)?$", s) is not None

Um die Leistung zu verbessern, kompilieren Sie den regulären Ausdruck, wenn Sie dies häufig tun, nur einmal mit re.compile().

22
Greg Hewgill

Die richtige RegEx-Lösung würde die Ideen von Greg Hewgill und Nowell kombinieren, jedoch keine globale Variable verwenden. Sie können dies erreichen, indem Sie der Methode ein Attribut hinzufügen. Ich weiß auch, dass es verpönt ist, Importe in eine Methode zu setzen, aber ich möchte einen "Lazy Module" -Effekt wie http://peak.telecommunity.com/DevCenter/Importing#lazy-imports

edit: Meine bevorzugte Technik ist es, ausschließlich Methoden des String-Objekts zu verwenden.

#!/usr/bin/env python

# Uses exclusively methods of the String object
def isInteger(i):
    i = str(i)
    return i=='0' or (i if i.find('..') > -1 else i.lstrip('-+').rstrip('0').rstrip('.')).isdigit()

# Uses re module for regex
def isIntegre(i):
    import re
    if not hasattr(isIntegre, '_re'):
        print("I compile only once. Remove this line when you are confident in that.")
        isIntegre._re = re.compile(r"[-+]?\d+(\.0*)?$")
    return isIntegre._re.match(str(i)) is not None

# When executed directly run Unit Tests
if __== '__main__':
    for obj in [
                # integers
                0, 1, -1, 1.0, -1.0,
                '0', '0.','0.0', '1', '-1', '+1', '1.0', '-1.0', '+1.0',
                # non-integers
                1.1, -1.1, '1.1', '-1.1', '+1.1',
                '1.1.1', '1.1.0', '1.0.1', '1.0.0',
                '1.0.', '1..0', '1..',
                '0.0.', '0..0', '0..',
                'one', object(), (1,2,3), [1,2,3], {'one':'two'}
            ]:
        # Notice the integre uses 're' (intended to be humorous)
        integer = ('an integer' if isInteger(obj) else 'NOT an integer')
        integre = ('an integre' if isIntegre(obj) else 'NOT an integre')
        # Make strings look like strings in the output
        if isinstance(obj, str):
            obj = ("'%s'" % (obj,))
        print("%30s is %14s is %14s" % (obj, integer, integre))

Und für die weniger abenteuerlichen Mitglieder der Klasse ist hier die Ausgabe:

I compile only once. Remove this line when you are confident in that.
                             0 is     an integer is     an integre
                             1 is     an integer is     an integre
                            -1 is     an integer is     an integre
                           1.0 is     an integer is     an integre
                          -1.0 is     an integer is     an integre
                           '0' is     an integer is     an integre
                          '0.' is     an integer is     an integre
                         '0.0' is     an integer is     an integre
                           '1' is     an integer is     an integre
                          '-1' is     an integer is     an integre
                          '+1' is     an integer is     an integre
                         '1.0' is     an integer is     an integre
                        '-1.0' is     an integer is     an integre
                        '+1.0' is     an integer is     an integre
                           1.1 is NOT an integer is NOT an integre
                          -1.1 is NOT an integer is NOT an integre
                         '1.1' is NOT an integer is NOT an integre
                        '-1.1' is NOT an integer is NOT an integre
                        '+1.1' is NOT an integer is NOT an integre
                       '1.1.1' is NOT an integer is NOT an integre
                       '1.1.0' is NOT an integer is NOT an integre
                       '1.0.1' is NOT an integer is NOT an integre
                       '1.0.0' is NOT an integer is NOT an integre
                        '1.0.' is NOT an integer is NOT an integre
                        '1..0' is NOT an integer is NOT an integre
                         '1..' is NOT an integer is NOT an integre
                        '0.0.' is NOT an integer is NOT an integre
                        '0..0' is NOT an integer is NOT an integre
                         '0..' is NOT an integer is NOT an integre
                         'one' is NOT an integer is NOT an integre
<object object at 0x103b7d0a0> is NOT an integer is NOT an integre
                     (1, 2, 3) is NOT an integer is NOT an integre
                     [1, 2, 3] is NOT an integer is NOT an integre
                {'one': 'two'} is NOT an integer is NOT an integre
16
Bruno Bronosky

str.isdigit() sollte den Trick machen.

Beispiele:

str.isdigit("23") ## True str.isdigit("abc") ## False str.isdigit("23.4") ## False

5
Catbuilts

Dem Ansatz von Greg Hewgill fehlten einige Komponenten: Das führende "^" sollte nur mit dem Anfang der Zeichenfolge übereinstimmen und das Re vor dem Kompilieren. Mit diesem Ansatz können Sie jedoch einen Versuch vermeiden: Ausnahme:

import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
    return INT_RE.match(str(s)) is not None

Mich würde interessieren, warum Sie versuchen, es zu vermeiden: außer?

3
Nowell
>>> "+7".lstrip("-+").isdigit()
True
>>> "-7".lstrip("-+").isdigit()
True
>>> "7".lstrip("-+").isdigit()
True
>>> "13.4".lstrip("-+").isdigit()
False

Ihre Funktion wäre also:

def is_int(val):
   return val[1].isdigit() and val.lstrip("-+").isdigit()
2
alkos333

Meiner Ansicht nach

s.startswith('-') and s[1:].isdigit()

wäre besser umzuschreiben in:

s.replace('-', '').isdigit()

weil s [1:] auch eine neue Zeichenfolge erstellt

Aber eine viel bessere Lösung ist

s.lstrip('+-').isdigit()
2

Shavais 'Post hat mir wirklich sehr gefallen, aber ich habe noch einen Testfall hinzugefügt (& die eingebaute isdigit () - Funktion):

def isInt_loop(v):
    v = str(v).strip()
    # swapping '0123456789' for '9876543210' makes nominal difference (might have because '1' is toward the beginning of the string)
    numbers = '0123456789'
    for i in v:
        if i not in numbers:
            return False
    return True

def isInt_Digit(v):
    v = str(v).strip()
    return v.isdigit()

und es schlägt wesentlich konsequent die Zeiten des Restes:

timings..
isInt_try:   0.4628
isInt_str:   0.3556
isInt_re:    0.4889
isInt_re2:   0.2726
isInt_loop:   0.1842
isInt_Digit:   0.1577

mit normalem 2.7-Python:

$ python --version
Python 2.7.10

Die beiden von mir hinzugefügten Testfälle (isInt_loop und isInt_digit) bestehen genau dieselben Testfälle (beide akzeptieren nur vorzeichenlose Ganzzahlen), aber ich dachte, die Leute könnten klüger sein, wenn sie die Stringimplementierung (isInt_loop) gegenüber dem eingebauten isdigit ändern () - Funktion, also habe ich sie hinzugefügt, auch wenn die Ausführungszeit etwas unterschiedlich ist. (und beide Methoden schlagen alles andere um einiges, behandeln aber nicht das Extra-Zeug: "./+/-")

Ich fand es auch interessant zu bemerken, dass der Regex (isInt_re2-Methode) den Stringvergleich im selben Test schlug, der 2012 von Shavais (derzeit 2018) durchgeführt wurde. Vielleicht wurden die Regex-Bibliotheken verbessert?

1
brw59

Dies ist wahrscheinlich der einfachste und pythonic Weg, um es meiner Meinung nach anzugehen. Ich habe diese Lösung nicht gesehen und ist im Grunde dasselbe wie die Regex, aber ohne Regex. 

def is_int(test):
    import string
    return not (set(test) - set(string.digits))
1
Xenlyte

Hier ist eine Funktion, die analysiert, ohne Fehler zu erzeugen. Es behandelt offensichtliche Fälle, die bei einem Fehler None zurückgegeben werden (verarbeitet standardmäßig bis zu 2000 '-/+' Zeichen in CPython!)

#!/usr/bin/env python

def get_int(number):
    splits = number.split('.')
    if len(splits) > 2:
        # too many splits
        return None
    if len(splits) == 2 and splits[1]:
        # handle decimal part recursively :-)
        if get_int(splits[1]) != 0:
            return None

    int_part = splits[0].lstrip("+")
    if int_part.startswith('-'):
        # handle minus sign recursively :-)
        return get_int(int_part[1:]) * -1
    # successful 'and' returns last truth-y value (cast is always valid)
    return int_part.isdigit() and int(int_part)

Einige Tests:

tests = ["0", "0.0", "0.1", "1", "1.1", "1.0", "-1", "-1.1", "-1.0", "-0", "--0", "---3", '.3', '--3.', "+13", "+-1.00", "--+123", "-0.000"]

for t in tests:
    print "get_int(%s) = %s" % (t, get_int(str(t)))

Ergebnisse:

get_int(0) = 0
get_int(0.0) = 0
get_int(0.1) = None
get_int(1) = 1
get_int(1.1) = None
get_int(1.0) = 1
get_int(-1) = -1
get_int(-1.1) = None
get_int(-1.0) = -1
get_int(-0) = 0
get_int(--0) = 0
get_int(---3) = -3
get_int(.3) = None
get_int(--3.) = 3
get_int(+13) = 13
get_int(+-1.00) = -1
get_int(--+123) = 123
get_int(-0.000) = 0

Für Ihre Bedürfnisse können Sie verwenden:

def int_predicate(number):
     return get_int(number) is not None
1
Reut Sharabani

Ich habe eine Möglichkeit, die int nicht verwendet und keine Ausnahme auslösen sollte, es sei denn, die Zeichenfolge repräsentiert keine Zahl

float(number)==float(number)//1

Es sollte für jede Art von Zeichenkette funktionieren, die Float, positive, negative, technische Notation akzeptiert.

0
agomcas

Ich denke, die Frage hängt mit der Geschwindigkeit zusammen, da der Versuch/außer eine Zeitstrafe hat:

 Testdaten

Zuerst habe ich eine Liste mit 200 Zeichenketten, 100 fehlerhaften Zeichenfolgen und 100 numerischen Zeichenfolgen erstellt.

from random import shuffle
numbers = [u'+1'] * 100
nonumbers = [u'1abc'] * 100
testlist = numbers + nonumbers
shuffle(testlist)
testlist = np.array(testlist)

 numpy Lösung (funktioniert nur mit Arrays und Unicode)

np.core.defchararray.isnumeric kann auch mit Unicode-Strings np.core.defchararray.isnumeric(u'+12') arbeiten, gibt jedoch ein Array zurück. Dies ist eine gute Lösung, wenn Sie Tausende von Konvertierungen durchführen müssen und fehlende Daten oder nicht numerische Daten haben.

import numpy as np
%timeit np.core.defchararray.isnumeric(testlist)
10000 loops, best of 3: 27.9 µs per loop # 200 numbers per loop

versuch/ausgenommen

def check_num(s):
  try:
    int(s)
    return True
  except:
    return False

def check_list(l):
  return [check_num(e) for e in l]

%timeit check_list(testlist)
1000 loops, best of 3: 217 µs per loop # 200 numbers per loop

Die numpy-Lösung scheint viel schneller zu sein. 

0
Carlos Vega