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 / exception-Mechanismus zu verwenden?
is_int('3.14') = False
is_int('-7') = True
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 / exception-Mechanismus zu verwenden?
is_int('3.14') = False
is_int('-7') = True
Antworten:
Wenn Sie sich wirklich nur darüber ärgern, try/except
s ü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 viel mehr Code geben, um alle Zeichenfolgen, die Python als Ganzzahlen betrachtet, genau abzudecken. Ich sage, sei in diesem Fall einfach pythonisch.
>>> print RepresentsInt(10.0)
True
>>> print RepresentsInt(10.06)
True
mit positiven ganzen Zahlen könnten Sie verwenden .isdigit
:
>>> '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, das dem int
Casting in diesem Sinne ähnlich ist .
bearbeiten :
def check_int(s):
if s[0] in ('-', '+'):
return s[1:].isdigit()
return s.isdigit()
u'²'.isdigit()
ist wahr, int(u'²')
löst aber ValueError aus. Verwenden Sie u.isdecimal()
stattdessen. str.isdigit()
ist vom Gebietsschema abhängig von Python 2.
check_int('')
wird eine Ausnahme False
Weißt du, ich habe festgestellt (und ich habe dies immer wieder getestet), dass try / without aus irgendeinem Grund nicht so gut funktioniert. Ich versuche häufig verschiedene Methoden, um Dinge zu tun, und ich glaube nicht, dass ich jemals eine Methode gefunden habe, die try / außer verwendet, um die besten der getesteten zu erzielen. Tatsächlich scheint es mir, dass diese Methoden normalerweise in der Nähe der am schlimmsten, wenn nicht am schlimmsten. Nicht in jedem Fall, aber in vielen Fällen. Ich weiß, dass viele Leute sagen, es sei der "pythonische" Weg, aber das ist ein Bereich, in dem ich mich von ihnen trenne. Für mich ist es weder sehr performant noch sehr elegant, daher verwende ich es eher nur zum Abfangen und Melden von Fehlern.
Ich wollte mich darüber beschweren, dass PHP, Perl, Ruby, C und sogar die verdammte Shell einfache Funktionen zum Testen eines Strings auf Integer-Hood haben, aber die gebotene Sorgfalt bei der Überprüfung dieser Annahmen hat mich gestolpert! Anscheinend ist dieser Mangel eine häufige Krankheit.
Hier ist eine schnelle und schmutzige Bearbeitung von Brunos Beitrag:
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 __name__ == '__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 Ergebnisse des Leistungsvergleichs:
timings..
isInt_try: 0.6426
isInt_str: 0.7382
isInt_re: 1.1156
isInt_re2: 0.5344
check_int: 0.3452
Die AC-Methode könnte es einmal durch scannen und fertig sein. Eine AC-Methode, die den String einmal durchsucht, wäre meiner Meinung nach das Richtige.
BEARBEITEN:
Ich habe den obigen Code so aktualisiert, dass er in Python 3.5 funktioniert, die Funktion check_int aus der aktuell am häufigsten bewerteten Antwort enthält und den derzeit beliebtesten regulären Ausdruck verwendet, den ich zum Testen auf Integer-Hood finden kann. Diese Regex lehnt Zeichenfolgen wie 'abc 123' ab. Ich habe 'abc 123' als Testwert hinzugefügt.
Es ist sehr interessant für mich, an dieser Stelle zu bemerken, dass KEINE der getesteten Funktionen, einschließlich der try-Methode, der beliebten check_int-Funktion und des beliebtesten regulären Ausdrucks zum Testen auf Integer-Hood, die richtigen Antworten für alle zurückgibt Testwerte (je nachdem, was Sie für die richtigen Antworten halten; siehe die Testergebnisse unten).
Die integrierte Funktion int () schneidet den Bruchteil einer Gleitkommazahl stillschweigend ab und gibt den ganzzahligen Teil vor der Dezimalstelle zurück, es sei denn, die Gleitkommazahl wird zuerst in eine Zeichenfolge konvertiert.
Die Funktion check_int () gibt false für Werte wie 0.0 und 1.0 (die technisch gesehen ganze Zahlen sind) und true für Werte wie '06' zurück.
Hier sind die aktuellen (Python 3.5) Testergebnisse:
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 |
Gerade habe ich 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 es gibt auch wahr für '06' zurück, also. Such dir dein Gift aus, denke ich.
try
ist effizienter: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
str.isdigit()
sollte den Trick machen.
Beispiele:
str.isdigit("23") ## True
str.isdigit("abc") ## False
str.isdigit("23.4") ## False
EDIT : Wie @BuzzMoschetti wies darauf hin, auf diese Weise wird für die fehler minus Zahl (zB „-23“ ). Wenn Ihre input_num kleiner als 0 sein kann, verwenden Sie re.sub (regex_search, regex_replace, content), bevor Sie str.isdigit () anwenden . Zum Beispiel:
import re
input_num = "-23"
input_num = re.sub("^-", "", input_num) ## "^" indicates to remove the first "-" only
str.isdigit(input_num) ## True
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
Wenn Sie dies häufig tun, kompilieren Sie den regulären Ausdruck nur einmal, um die Leistung zu verbessern re.compile()
.
Die richtige RegEx-Lösung würde die Ideen von Greg Hewgill und Nowell kombinieren, aber 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 einzufügen, aber ich strebe einen "Lazy Module" -Effekt wie http://peak.telecommunity.com/DevCenter/Importing#lazy-imports an
edit: Meine bisherige Lieblingstechnik besteht darin, 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 __name__ == '__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 abenteuerlustigen 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
>>> "+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()
Greg Hewgills Ansatz fehlten einige Komponenten: das führende "^", das nur zum Anfang des Strings passte, und das Kompilieren des Re im Voraus. Mit diesem Ansatz können Sie jedoch einen Versuch vermeiden: außer:
import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
return INT_RE.match(str(s)) is not None
Es würde mich interessieren, warum Sie versuchen, einen Versuch zu vermeiden: außer?
Ich muss das die ganze Zeit tun, und ich habe eine milde, aber zugegebenermaßen irrationale Abneigung gegen die Verwendung des Try / Except-Musters. Ich benutze das:
all([xi in '1234567890' for xi in x])
Es werden keine negativen Zahlen berücksichtigt, daher können Sie ein Minuszeichen (falls vorhanden) entfernen und dann prüfen, ob das Ergebnis Ziffern von 0 bis 9 enthält:
all([xi in '1234567890' for xi in x.replace('-', '', 1)])
Sie können x auch an str () übergeben, wenn Sie nicht sicher sind, ob die Eingabe eine Zeichenfolge ist:
all([xi in '1234567890' for xi in str(x).replace('-', '', 1)])
Es gibt mindestens zwei (Rand-?) Fälle, in denen dies auseinander fällt:
type(1E2)
gibt, <class 'float'>
während type(10^2)
gibt <class 'int'>
.Es funktioniert also nicht für jede mögliche Eingabe, aber wenn Sie wissenschaftliche Notation, Exponentialnotation und leere Zeichenfolgen ausschließen können, ist es eine einzeilige OK-Prüfung, die zurückgibt, False
wenn x keine Ganzzahl und True
x eine Ganzzahl ist.
Ich weiß nicht, ob es pythonisch ist, aber es ist eine Zeile und es ist relativ klar, was der Code tut.
all(xi in '1234567890' for xi in x])
Muster eher so aussieht, als würde man um Erlaubnis bitten, über den Rasen zu gehen. Ich bin nicht begeistert, ein Erlaubnisgeber zu sein, aber hier sind wir.
Ich glaube
s.startswith('-') and s[1:].isdigit()
wäre besser umzuschreiben an:
s.replace('-', '').isdigit()
weil s [1:] auch einen neuen String erstellt
Aber viel bessere Lösung ist
s.lstrip('+-').isdigit()
replace
macht? Auch dies wird 5-2
zum Beispiel falsch akzeptiert .
s='-'
Shavais 'Post hat mir sehr gut gefallen, aber ich habe noch einen Testfall hinzugefügt (und die eingebaute Funktion isdigit ()):
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 deutlich die Zeiten der anderen:
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 normaler 2.7 Python:
$ python --version
Python 2.7.10
Die beiden von mir hinzugefügten Testfälle (isInt_loop und isInt_digit) bestehen genau die gleichen Testfälle (beide akzeptieren nur vorzeichenlose Ganzzahlen), aber ich dachte, die Leute könnten klüger sein, wenn sie die String-Implementierung (isInt_loop) ändern, im Gegensatz zum eingebauten isdigit () Funktion, also habe ich es aufgenommen, obwohl es einen kleinen Unterschied in der Ausführungszeit gibt. (und beide Methoden schlagen alles andere um ein Vielfaches, aber behandeln Sie nicht die zusätzlichen Dinge: "./+/-")
Außerdem fand ich es interessant festzustellen, dass der Regex (isInt_re2-Methode) den String-Vergleich im selben Test, den Shavais 2012 (derzeit 2018) durchgeführt hat, übertroffen hat. Vielleicht wurden die Regex-Bibliotheken verbessert?
Dies ist meiner Meinung nach wahrscheinlich der einfachste und pythonischste Weg, sich dem anzunähern. Ich habe diese Lösung nicht gesehen und sie ist im Grunde die gleiche wie die Regex, aber ohne die Regex.
def is_int(test):
import string
return not (set(test) - set(string.digits))
set(input_string) == set(string.digits)
wenn wir überspringen '-+ '
am Anfang und .0
, E-1
am Ende.
Hier ist eine Funktion, die analysiert, ohne Fehler auszulösen. Es behandelt offensichtliche Fälle, in denen None
ein Fehler auftritt (behandelt 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
Ich schlage folgendes vor:
import ast
def is_int(s):
return isinstance(ast.literal_eval(s), int)
Aus den Dokumenten :
Bewerten Sie sicher einen Ausdrucksknoten oder eine Zeichenfolge, die ein Python-Literal oder eine Containeranzeige enthält. Die bereitgestellte Zeichenfolge oder der bereitgestellte Knoten darf nur aus den folgenden Python-Literalstrukturen bestehen: Zeichenfolgen, Bytes, Zahlen, Tupel, Listen, Diktate, Mengen, Boolesche Werte und Keine.
Ich sollte beachten, dass dies eine ValueError
Ausnahme auslöst, wenn es gegen etwas aufgerufen wird, das kein Python-Literal darstellt. Da die Frage nach einer Lösung ohne Versuch / Ausnahme gestellt wurde, habe ich eine Lösung vom Typ Kobayashi-Maru dafür:
from ast import literal_eval
from contextlib import suppress
def is_int(s):
with suppress(ValueError):
return isinstance(literal_eval(s), int)
return False
¯ \ _ (ツ) _ / ¯
Ich habe eine Möglichkeit, die int überhaupt 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 Zeichenfolge funktionieren, die float akzeptiert, positive, negative, technische Notation ...
Ich denke, die Frage hängt mit der Geschwindigkeit zusammen, da der Versuch / außer eine Zeitstrafe hat:
Zuerst habe ich eine Liste mit 200 Zeichenfolgen, 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)
np.core.defchararray.isnumeric kann auch mit Unicode-Zeichenfolgen arbeiten np.core.defchararray.isnumeric(u'+12')
, gibt jedoch ein Array zurück. Es ist also eine gute Lösung, wenn Sie Tausende von Konvertierungen durchführen müssen und fehlende 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
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
Scheint, dass numpy Lösung viel schneller ist.
Wenn Sie nur niedrigere ASCII-Ziffern akzeptieren möchten, finden Sie hier folgende Tests:
Python 3.7+: (u.isdecimal() and u.isascii())
Python <= 3.6: (u.isdecimal() and u == str(int(u)))
Andere Antworten schlagen vor, .isdigit()
oder zu verwenden, .isdecimal()
aber beide enthalten einige Unicode-Zeichen wie '٢'
( u'\u0662'
):
u = u'\u0662' # '٢'
u.isdigit() # True
u.isdecimal() # True
u.isascii() # False (Python 3.7+ only)
u == str(int(u)) # False
int()
.
Äh .. Versuchen Sie dies:
def int_check(a):
if int(a) == a:
return True
else:
return False
Dies funktioniert, wenn Sie keine Zeichenfolge eingeben, die keine Zahl ist.
Und außerdem (ich habe vergessen, den Teil zur Nummernprüfung einzufügen) gibt es eine Funktion, die prüft, ob die Zeichenfolge eine Zahl ist oder nicht. Es ist str.isdigit (). Hier ist ein Beispiel:
a = 2
a.isdigit()
Wenn Sie a.isdigit () aufrufen, wird True zurückgegeben.
2
zugewiesenen Wert a
.