Comment puis-je vérifier si une chaîne représente un int, sans utiliser try/except?

y a-t-il un moyen de savoir si une" chaîne 151970920 "représente un entier (par exemple, '3' , '-17' mais pas '3.14' ou 'asfasfas' ) sans utiliser un mécanisme try/except?

is_int('3.14') = False
is_int('-7')   = True
306
demandé sur Martin Thoma 2009-08-12 15:46:13

13 réponses

si vous êtes vraiment juste ennuyé à utiliser try/except s partout, s'il vous plaît écrivez juste une fonction helper:

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

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

ce sera beaucoup plus de code pour couvrir exactement toutes les chaînes que Python considère comme des entiers. Je dis juste d'être pythonique sur celui-ci.

286
répondu Triptych 2009-08-12 16:05:28

avec des entiers positifs que vous pouvez utiliser .isdigit :

>>> '16'.isdigit()
True

ça ne marche pas avec des entiers négatifs. supposons que vous puissiez essayer ce qui suit:

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

il ne fonctionnera pas avec le format '16.0' , qui est similaire au casting int dans ce sens.

modifier :

def check_int(s):
    if s[0] in ('-', '+'):
        return s[1:].isdigit()
    return s.isdigit()
497
répondu SilentGhost 2009-08-12 17:00:49

vous savez, j'ai trouvé (et j'ai testé cela encore et encore) que essayer/sauf ne fonctionne pas si bien, pour quelque raison que ce soit. J'essaie souvent plusieurs façons de faire les choses, et je ne pense pas que j'ai jamais trouvé une méthode qui utilise essayer/sauf pour effectuer le meilleur de ceux testés, en fait, il me semble que ces méthodes ont généralement sortir près du pire, si pas le pire. Pas dans tous les cas, mais dans de nombreux cas. Je sais que beaucoup de gens disent que c'est la façon" pythonique", mais c'est un domaine où j'partie des manières avec eux. Pour moi, ce n'est ni très performant ni très élégant, donc, j'ai tendance à l'utiliser seulement pour le piégeage et le rapport d'erreur.

j'allais me plaindre que PHP, perl, ruby, C, et même le putain de shell ont des fonctions simples pour tester une chaîne pour integer-hood, 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 édition rapide et sale du post 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

une méthode C pourrait le scanner une fois à travers, et être fait. Une méthode C qui scanne la chaîne une fois serait la bonne chose à faire, je pense.

EDIT:

j'ai mis à jour le code ci-dessus pour travailler en Python 3.5, et pour inclure la fonction check_int de la réponse actuellement la plus votée, et pour utiliser la plus populaire regex que je peux trouver pour le test de la hotte entière. Ce regex 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 point, QU'aucune des fonctions testées, y compris la méthode d'essai, la fonction check_int populaire, et le regex le plus populaire pour l'essai pour le nombre entier-hood, retourner les réponses correctes pour toutes les valeurs de test (Eh bien, selon ce que vous pensez que les réponses correctes sont; Voir les résultats de test 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, à moins que le nombre à virgule flottante ne soit 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 courants (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

fonctionne presque aussi bien que check_int (0.3486) et retourne true pour des valeurs comme 1.0 et 0.0 et +1.0 et 0. et. 0 et ainsi de suite. Mais il retourne aussi vrai pour '06', donc. Choisis ton poison, je suppose.

64
répondu Shavais 2018-09-05 17:40:51

utiliser une expression régulière:

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

si vous devez accepter les fractions décimales aussi:

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

pour une meilleure performance si vous faites cela souvent, compilez l'expression régulière une seule fois en utilisant re.compile() .

20
répondu Greg Hewgill 2009-08-12 12:01:52

la bonne solution de RegEx combinerait les idées de Greg Hewgill et Nowell, mais n'utiliserait pas une variable globale. Vous pouvez accomplir ceci en attachant un attribut à la méthode. En outre, je sais qu'il est mal vu de mettre les importations dans une méthode, mais ce que je vais pour est un" module paresseux "effet comme http://peak.telecommunity.com/DevCenter/Importing#lazy-imports

edit: ma technique préférée jusqu'à présent est d'utiliser exclusivement des 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 le résultat:

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
15
répondu Bruno Bronosky 2018-05-02 16:26:35

L'approche de Greg Hewgill manquait quelques composants: le premier " ^ " pour correspondre seulement au début de la chaîne, et la compilation de l'ed à l'avance. Mais cette approche vous permettra d'éviter d'essayer: exepté:

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

je serais intéressé pourquoi vous essayez d'éviter d'essayer: sauf?

3
répondu Nowell 2009-08-12 12:37:28
>>> "+7".lstrip("-+").isdigit()
True
>>> "-7".lstrip("-+").isdigit()
True
>>> "7".lstrip("-+").isdigit()
True
>>> "13.4".lstrip("-+").isdigit()
False

donc votre fonction serait:

def is_int(val):
   return val[1].isdigit() and val.lstrip("-+").isdigit()
2
répondu alkos333 2012-04-27 13:12:51

je pense

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

serait mieux réécrire en:

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

parce que s [1:] crée aussi une nouvelle chaîne

mais la meilleure solution est

s.lstrip('+-').isdigit()
2
répondu Vladyslav Savchenko 2017-05-08 18:59:52

c'est probablement la façon la plus directe et pythonique de l'aborder à mon avis. Je n'ai pas vu cette solution et c'est essentiellement la même que celle du regex, mais sans le regex.

def is_int(test):
    import string
    return not (set(test) - set(string.digits))
1
répondu Xenlyte 2012-10-12 17:22:21

Voici une fonction qui part sans soulever d'erreurs. Il gère les cas évidents retourne None en cas 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 essais:

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
1
répondu Reut Sharabani 2015-03-29 15:51:10

j'ai vraiment aimé le post de Shavais, mais j'ai ajouté un cas de test supplémentaire (&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 régulièrement de manière significative les 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

utilisant 2.7 python 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 que des entiers non signés), mais j'ai pensé que les gens pourraient être plus intelligent de modifier l'implémentation de la chaîne (isInt_loop) par opposition à la fonction isdigit () intégrée, 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 tout le reste par beaucoup, mais ne manipulez pas les choses supplémentaires:"./ + / - ")

aussi, j'ai trouvé intéressant de noter que regex (méthode isInt_re2) a battu la comparaison de chaîne dans le même test qui a été effectué par Shavais en 2012 (actuellement 2018). Peut-être le regex les bibliothèques ont été améliorées?

1
répondu brw59 2018-06-15 23:21:27

j'ai une possibilité qui n'utilise pas int du tout, et ne devrait pas soulever une exception à moins que la chaîne ne représente pas un nombre

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

il devrait fonctionner pour n'importe quel type de chaîne qui flottent accepte, positive, négative, la notation d'ingénierie...

0
répondu agomcas 2015-03-13 12:52:31

Uh.. 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 numéro.

et aussi (j'ai oublié de mettre la partie de contrôle de nombre. ), il y a une fonction de vérifier si la chaîne est un nombre ou non. Il est str.appel isdigit(). Voici un exemple:

a = 2
a.isdigit()

si vous appelez A. isdigit (), it will return True.

-5
répondu HaulCozen 2012-03-11 11:56:33