Existe-t-il un moyen de savoir si une chaîne représente un entier (par exemple '3'
, '-17'
mais pas '3.14'
ou 'asfasfas'
) sans utiliser de mécanisme try / except?
is_int('3.14') = False
is_int('-7') = True
Existe-t-il un moyen de savoir si une chaîne représente un entier (par exemple '3'
, '-17'
mais pas '3.14'
ou 'asfasfas'
) sans utiliser de mécanisme try / except?
is_int('3.14') = False
is_int('-7') = True
Réponses:
Si vous êtes vraiment ennuyé d'utiliser try/except
s partout, veuillez simplement écrire une fonction d'aide:
def RepresentsInt(s):
try:
int(s)
return True
except ValueError:
return False
>>> print RepresentsInt("+123")
True
>>> print RepresentsInt("10.0")
False
Cela va être BEAUCOUP plus de code pour couvrir exactement toutes les chaînes que Python considère comme des entiers. Je dis juste être pythonique sur celui-ci.
>>> print RepresentsInt(10.0)
True
>>> print RepresentsInt(10.06)
True
avec des entiers positifs, vous pouvez utiliser .isdigit
:
>>> '16'.isdigit()
True
cela ne fonctionne pas avec des entiers négatifs. supposons que vous puissiez essayer ce qui suit:
>>> s = '-17'
>>> s.startswith('-') and s[1:].isdigit()
True
cela ne fonctionnera pas avec le '16.0'
format, qui est similaire au int
casting dans ce sens.
modifier :
def check_int(s):
if s[0] in ('-', '+'):
return s[1:].isdigit()
return s.isdigit()
u'²'.isdigit()
est vrai mais int(u'²')
déclenche ValueError. Utilisez u.isdecimal()
plutôt. str.isdigit()
dépend de l'environnement local sur Python 2.
check_int('')
False
Vous savez, j'ai trouvé (et je l'ai testé maintes et maintes fois) que try / except ne fonctionne pas très bien, quelle qu'en soit la raison. J'essaie fréquemment plusieurs façons de faire les choses, et je ne pense pas avoir jamais trouvé de méthode qui utilise try / except pour exécuter les meilleures de celles testées, en fait, il me semble que ces méthodes sont généralement proches de la le pire, sinon le pire. Pas dans tous les cas, mais dans de nombreux cas. Je sais que beaucoup de gens disent que c'est la voie "Pythonique", mais c'est un domaine où je me sépare d'eux. Pour moi, ce n'est ni très performant ni très élégant, donc j'ai tendance à ne l'utiliser que pour le piégeage d'erreurs et la génération de rapports.
J'allais reprocher que PHP, perl, ruby, C et même le shell flippant ont des fonctions simples pour tester une chaîne pour le nombre entier, mais la diligence raisonnable dans la vérification de ces hypothèses m'a fait trébucher! Apparemment, ce manque est une maladie courante.
Voici une modification rapide et sale du message de Bruno:
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))
Voici les résultats de la comparaison des performances:
timings..
isInt_try: 0.6426
isInt_str: 0.7382
isInt_re: 1.1156
isInt_re2: 0.5344
check_int: 0.3452
La méthode AC pourrait le scanner une fois et être terminée. La méthode AC qui balaye une fois la chaîne serait la bonne chose à faire, je pense.
ÉDITER:
J'ai mis à jour le code ci-dessus pour fonctionner en Python 3.5, et pour inclure la fonction check_int à partir de la réponse actuellement la plus votée, et pour utiliser l'expression régulière la plus populaire que je puisse trouver pour tester le capotage entier. Cette expression régulière rejette les chaînes comme «abc 123». J'ai ajouté «abc 123» comme valeur de test.
Il est très intéressant pour moi de noter, à ce stade, que AUCUNE des fonctions testées, y compris la méthode try, la fonction check_int populaire, et l'expression rationnelle la plus populaire pour tester le nombre entier, renvoie les réponses correctes pour tous les valeurs du test (enfin, selon ce que vous pensez être les bonnes réponses; voir les résultats du test ci-dessous).
La fonction int () intégrée tronque silencieusement la partie fractionnaire d'un nombre à virgule flottante et renvoie la partie entière avant la décimale, sauf si le nombre à virgule flottante est d'abord converti en chaîne.
La fonction check_int () retourne false pour des valeurs comme 0.0 et 1.0 (qui sont techniquement des entiers) et retourne true pour des valeurs comme '06'.
Voici les résultats des tests actuels (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 |
Tout à l'heure, j'ai essayé d'ajouter cette fonction:
def isInt_float(s):
try:
return float(str(s)).is_integer()
except:
return False
Il fonctionne presque aussi bien que check_int (0.3486) et il retourne vrai pour des valeurs comme 1.0 et 0.0 et +1.0 et 0. et .0 et ainsi de suite. Mais cela renvoie également vrai pour '06', donc. Choisissez votre poison, je suppose.
try
est plus efficace: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
str.isdigit()
devrait faire l'affaire.
Exemples:
str.isdigit("23") ## True
str.isdigit("abc") ## False
str.isdigit("23.4") ## False
EDIT : Comme l'a souligné @BuzzMoschetti, cette méthode échouera pour le nombre moins (par exemple, "-23" ). Dans le cas où votre numéro_entrée peut être inférieur à 0, utilisez re.sub (regex_search, regex_replace, contents) avant d'appliquer str.isdigit () . Par exemple:
import re
input_num = "-23"
input_num = re.sub("^-", "", input_num) ## "^" indicates to remove the first "-" only
str.isdigit(input_num) ## True
Utilisez une expression régulière:
import re
def RepresentsInt(s):
return re.match(r"[-+]?\d+$", s) is not None
Si vous devez également accepter des fractions décimales:
def RepresentsInt(s):
return re.match(r"[-+]?\d+(\.0*)?$", s) is not None
Pour de meilleures performances si vous le faites souvent, compilez l'expression régulière une seule fois en utilisant re.compile()
.
La bonne solution RegEx combinerait les idées de Greg Hewgill et Nowell, mais n'utiliserait pas de variable globale. Vous pouvez accomplir cela en attachant un attribut à la méthode. De plus, je sais qu'il est mal vu de placer les importations dans une méthode, mais ce que je veux, c'est un effet de "module paresseux" comme http://peak.telecommunity.com/DevCenter/Importing#lazy-imports
edit: Ma technique préférée jusqu'à présent est d'utiliser exclusivement les méthodes de l'objet String.
#!/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))
Et pour les membres les moins aventureux de la classe, voici la sortie:
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
Votre fonction serait donc:
def is_int(val):
return val[1].isdigit() and val.lstrip("-+").isdigit()
L'approche de Greg Hewgill manquait de quelques composants: le "^" de tête pour ne correspondre qu'au début de la chaîne et la compilation préalable. Mais cette approche vous permettra d'éviter un essai: exept:
import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
return INT_RE.match(str(s)) is not None
Je serais curieux de savoir pourquoi vous essayez d'éviter d'essayer: sauf?
Je dois le faire tout le temps, et j'ai une aversion douce mais certes irrationnelle à utiliser le modèle try / except. J'utilise ceci:
all([xi in '1234567890' for xi in x])
Il ne prend pas en charge les nombres négatifs, vous pouvez donc supprimer un signe moins (le cas échéant), puis vérifier si le résultat comprend des chiffres compris entre 0 et 9:
all([xi in '1234567890' for xi in x.replace('-', '', 1)])
Vous pouvez également passer x à str () si vous n'êtes pas sûr que l'entrée est une chaîne:
all([xi in '1234567890' for xi in str(x).replace('-', '', 1)])
Il y a au moins deux cas (marginaux?) Où cela s'effondre:
type(1E2)
donne <class 'float'>
alors que type(10^2)
donne <class 'int'>
.Donc, cela ne fonctionnera pas pour toutes les entrées possibles, mais si vous pouvez exclure la notation scientifique, la notation exponentielle et les chaînes vides, c'est une vérification OK sur une ligne qui retourne False
si x n'est pas un entier et True
si x est un entier.
Je ne sais pas si c'est pythonique, mais c'est une seule ligne, et c'est relativement clair ce que fait le code.
all(xi in '1234567890' for xi in x])
modèle ressemble plus à demander la permission de traverser la pelouse. Je ne suis pas ravi d'être un demandeur de permission, mais nous y voilà.
je pense
s.startswith('-') and s[1:].isdigit()
il serait préférable de réécrire:
s.replace('-', '').isdigit()
car s [1:] crée également une nouvelle chaîne
Mais une bien meilleure solution est
s.lstrip('+-').isdigit()
replace
? En outre, cela acceptera incorrectement 5-2
, par exemple.
s='-'
J'ai vraiment aimé le post de Shavais, mais j'ai ajouté un cas de test de plus (et la fonction isdigit () intégrée):
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()
et il bat de manière significative le temps du reste:
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
en utilisant un python 2.7 normal:
$ python --version
Python 2.7.10
Les deux cas de test que j'ai ajoutés (isInt_loop et isInt_digit) passent exactement les mêmes cas de test (ils n'acceptent tous les deux que des entiers non signés), mais je pensais que les gens pourraient être plus intelligents avec la modification de l'implémentation de la chaîne (isInt_loop) par opposition à l'isdigit intégré (), donc je l'ai inclus, même s'il y a une légère différence dans le temps d'exécution. (et les deux méthodes battent beaucoup le reste, mais ne gèrent pas les trucs supplémentaires: "./+/-")
De plus, j'ai trouvé intéressant de noter que l'expression régulière (méthode isInt_re2) a battu la comparaison des cordes dans le même test qui a été effectué par Shavais en 2012 (actuellement 2018). Peut-être que les bibliothèques regex ont été améliorées?
C'est probablement la façon la plus simple et la plus pythonique de l'aborder à mon avis. Je n'ai pas vu cette solution et c'est fondamentalement la même que la regex, mais sans la regex.
def is_int(test):
import string
return not (set(test) - set(string.digits))
set(input_string) == set(string.digits)
si nous sauter '-+ '
au début du mois et .0
, E-1
à la fin.
Voici une fonction qui analyse sans générer d'erreurs. Il gère les cas évidents de retours en cas None
d'échec (gère jusqu'à 2000 signes '- / +' par défaut sur 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)
Quelques 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)))
Résultats:
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
Pour vos besoins, vous pouvez utiliser:
def int_predicate(number):
return get_int(number) is not None
Je suggère ce qui suit:
import ast
def is_int(s):
return isinstance(ast.literal_eval(s), int)
De la documentation :
Évaluez en toute sécurité un nœud d'expression ou une chaîne contenant un littéral Python ou un affichage de conteneur. La chaîne ou le nœud fourni ne peut être composé que des structures littérales Python suivantes: chaînes, octets, nombres, tuples, listes, dictés, ensembles, booléens et Aucun.
Je dois noter que cela lèvera une ValueError
exception lorsqu'il sera appelé contre tout ce qui ne constitue pas un littéral Python. Puisque la question demandait une solution sans try / except, j'ai une solution de type Kobayashi-Maru pour ça:
from ast import literal_eval
from contextlib import suppress
def is_int(s):
with suppress(ValueError):
return isinstance(literal_eval(s), int)
return False
¯ \ _ (ツ) _ / ¯
J'ai une possibilité qui n'utilise pas du tout int et ne devrait pas déclencher d'exception à moins que la chaîne ne représente pas un nombre
float(number)==float(number)//1
Cela devrait fonctionner pour tout type de chaîne acceptée par float, positive, négative, notation technique ...
Je suppose que la question est liée à la vitesse car l'essai / sauf a une pénalité de temps:
Tout d'abord, j'ai créé une liste de 200 chaînes, 100 chaînes défaillantes et 100 chaînes numériques.
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 peut également fonctionner avec des chaînes unicode np.core.defchararray.isnumeric(u'+12')
mais il retourne et tableau. C'est donc une bonne solution si vous devez effectuer des milliers de conversions et qu'il manque des données ou des données non numériques.
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
Semble que la solution numpy est beaucoup plus rapide.
Si vous souhaitez accepter uniquement les chiffres ascii inférieurs, voici des tests pour le faire:
Python 3.7+: (u.isdecimal() and u.isascii())
Python <= 3,6: (u.isdecimal() and u == str(int(u)))
D'autres réponses suggèrent d'utiliser .isdigit()
ou .isdecimal()
mais ces deux incluent des caractères unicode supérieurs tels que '٢'
( u'\u0662'
):
u = u'\u0662' # '٢'
u.isdigit() # True
u.isdecimal() # True
u.isascii() # False (Python 3.7+ only)
u == str(int(u)) # False
int()
.
Euh .. Essayez ceci:
def int_check(a):
if int(a) == a:
return True
else:
return False
Cela fonctionne si vous ne mettez pas une chaîne qui n'est pas un nombre.
Et aussi (j'ai oublié de mettre la partie de vérification des nombres.), Il y a une fonction vérifiant si la chaîne est un nombre ou non. C'est str.isdigit (). Voici un exemple:
a = 2
a.isdigit()
Si vous appelez a.isdigit (), il renverra True.
2
attribuée a
.