Come posso verificare se una stringa rappresenta un int, senza usare try / tranne?


467

C'è un modo per dire se una stringa rappresenta un numero intero (es. '3', '-17'Ma no '3.14'o 'asfasfas') Senza usare un meccanismo try / tranne?

is_int('3.14') = False
is_int('-7')   = True

23
Perché entrambi cercano di farlo "nel modo più duro?" Cosa c'è di sbagliato con try / tranne?
S. Lott

5
Sì, cosa c'è di sbagliato con try / tranne? Meglio chiedere perdono che permesso.
MK

53
Vorrei chiedere perché questa cosa semplice dovrebbe richiedere / tranne? Il sistema delle eccezioni è una bestia complessa, ma questo è un problema semplice.
Aivar,

13
@Aivar smette di diffondere FUD. Un singolo tentativo / tranne blocco non si avvicina nemmeno a "complesso".
Trittico

47
Non è proprio FUD, comunque. Scriveresti effettivamente 4 righe di codice, aspettandoti che esploda qualcosa, catturi quell'eccezione e fai il tuo default, invece di usare una riga.
Andersonvom,

Risposte:


398

Se sei davvero seccato di usare try/excepts dappertutto, ti preghiamo di scrivere una funzione di supporto:

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

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

Sarà MODO più codice per coprire esattamente tutte le stringhe che Python considera numeri interi. Dico solo essere pitone su questo.


124
Quindi è pitonico risolvere un semplice problema con un meccanismo complesso? C'è un algoritmo per rilevare la funzione interna scritta di int "int" - Non vedo perché non sia esposto come una funzione booleana.
Aivar,

79
@Aivar: questa funzione a 5 righe non è un meccanismo complesso.
Trittico

34
Tranne:>>> print RepresentsInt(10.0) True >>> print RepresentsInt(10.06) True
Dannid

5
Immagino sia "pythonic", nel senso che se Python pensa che la stringa sia un int, anche il tuo programma. Se Python cambia, lo stesso fa il tuo programma e senza cambiare una singola riga di codice. C'è del valore in questo. Potrebbe essere la cosa giusta da fare a seconda delle circostanze.
Shavais,

57
Non so perché questa sia la risposta accettata o abbia così tanti voti positivi, dal momento che questo è esattamente l'opposto di ciò che l'OP chiede.
FearlessFuture,

757

con numeri interi positivi puoi usare .isdigit:

>>> '16'.isdigit()
True

tuttavia non funziona con numeri interi negativi. supponiamo di poter provare quanto segue:

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

non funzionerà con il '16.0'formato, che è simile al intcasting in questo senso.

modifica :

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

6
questo non gestisce "+17" senza un caso speciale aggiuntivo.
Bryan Oakley,

1
Devi testare ENTRAMBI i casi: lambda s: s.isdigit () o (s.startswith ('-') e s [1:]. Isdigit ())
rob

4
@Roberto: certo che dovresti! e sono sicuro che sei in grado di farlo!
SilentGhost,

22
nota: u'²'.isdigit()è vero ma int(u'²')genera ValueError. Usa u.isdecimal()invece. str.isdigit()dipende dalle impostazioni locali di Python 2.
jfs

4
check_int('')solleverà un'eccezione invece di tornareFalse
wordbug

97

Sai, ho scoperto (e l'ho provato più volte) che provare / tranne non funziona così bene, per qualsiasi motivo. Provo spesso diversi modi di fare le cose, e non credo di aver mai trovato un metodo che usi try / tranne per eseguire il meglio di quelli testati, in effetti mi sembra che quei metodi di solito si siano avvicinati al peggio, se non il peggio. Non in tutti i casi, ma in molti casi. So che molte persone dicono che è il modo "Pythonic", ma questa è un'area in cui mi separo da loro. Per me, non è né molto performante né molto elegante, quindi, tendo a usarlo solo per il trapping e la segnalazione degli errori.

Avevo intenzione di lamentarmi del fatto che PHP, perl, ruby, C e persino il guscio impazzito hanno funzioni semplici per testare una stringa per un intero, ma la dovuta diligenza nel verificare quei presupposti mi ha fatto scattare! Apparentemente questa mancanza è una malattia comune.

Ecco una modifica veloce e sporca del post di 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))

Ecco i risultati del confronto delle prestazioni:

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

Il metodo AC potrebbe scansionarlo una volta attraverso ed essere fatto. Penso che il metodo AC che scansiona una volta la stringa sia la cosa giusta da fare.

MODIFICARE:

Ho aggiornato il codice sopra per funzionare in Python 3.5 e per includere la funzione check_int dalla risposta attualmente più votata e per usare l'attuale regex più popolare che posso trovare per i test per integer-hood. Questa regex rifiuta stringhe come 'abc 123'. Ho aggiunto "abc 123" come valore di prova.

È molto interessante per me notare, a questo punto, che NESSUNA delle funzioni testate, incluso il metodo try, la popolare funzione check_int e la regex più popolare per i test per integer-hood, restituiscono le risposte corrette per tutte le valori di test (bene, a seconda di cosa pensi siano le risposte corrette; vedi i risultati del test di seguito).

La funzione integrata int () tronca silenziosamente la parte frazionaria di un numero in virgola mobile e restituisce la parte intera prima del decimale, a meno che il numero in virgola mobile non venga prima convertito in una stringa.

La funzione check_int () restituisce false per valori come 0.0 e 1.0 (che tecnicamente sono numeri interi) e restituisce true per valori come '06'.

Ecco i risultati del test corrente (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   |

Proprio ora ho provato ad aggiungere questa funzione:

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

Si comporta quasi oltre a check_int (0.3486) e restituisce true per valori come 1.0 e 0.0 e +1.0 e 0. e .0 e così via. Ma ritorna vero anche per '06', quindi. Scegli il tuo veleno, immagino.


Forse parte di esso deriva dal fatto che un intero è un po 'arbitrario in sé. Un sistema di programmazione non può permettersi il lusso di supporre che sarà sempre una rappresentazione decimale. 0x4df, è un numero intero valido in alcuni punti e 0891 non è in altri. Temo di pensare a cosa potrebbe sorgere dato l'unicode in questo tipo di controlli.
PlexQ,

3
+1 per i tempi. Concordo sul fatto che tutta questa eccezione non è davvero elegante per una domanda così semplice. Ti aspetteresti un metodo di supporto
integrato

9
So che questa discussione è praticamente inattiva, ma +1 per considerare il tempo di esecuzione. La lunghezza della linea non è sempre indicativa della complessità sottostante; e certo, un tentativo / salvo potrebbe sembrare semplice (e facile da leggere, il che è altrettanto importante), ma è un'operazione costosa. Direi che la gerarchia delle preferenze dovrebbe essere sempre simile alla seguente: 1. Una soluzione esplicita di facile lettura (SilentGhost). 2. Una soluzione implicita di facile lettura (trittico). 3. Non ce ne sono tre.
Eric Humphrey,

1
Grazie per le vostre indagini approfondite su un argomento apparentemente insignificante. Andrò con isInt_str (), pythonic o no. Quello che mi assilla è che non ho trovato nulla sul significato di v.find ('..'). È una specie di sintassi di ricerca speciale o un caso limite di una stringa numerica?
JackLeEmmerdeur

3
Sì, un'analisi un po 'datata ma comunque davvero piacevole e pertinente. In Python 3.5 tryè più efficiente: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
Dave,

40

str.isdigit() dovrebbe fare il trucco.

Esempi:

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

EDIT : Come sottolineato da @BuzzMoschetti, in questo modo fallirà per il numero meno (ad esempio, "-23" ). Nel caso in cui input_num possa essere inferiore a 0, utilizzare re.sub (regex_search, regex_replace, contents) prima di applicare str.isdigit () . Per esempio:

import re
input_num = "-23"
input_num = re.sub("^-", "", input_num) ## "^" indicates to remove the first "-" only
str.isdigit(input_num) ## True

1
Perché -23 produce falso.
Buzz Moschetti,

1
@BuzzMoschetti hai ragione. Un modo rapido per risolvere è rimuovere il segno meno da re.replace (regex_search, regex_replace, contents) prima di applicare str.isdigit ()
Catbuilts

27

Usa un'espressione regolare:

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

Se devi accettare anche le frazioni decimali:

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

Per prestazioni migliori se lo fai spesso, compila l'espressione regolare solo una volta usando re.compile().


19
+1: rivela che questo è terribilmente complesso e costoso se confrontato con try / tranne.
S.Lott

2
Penso che questa sia essenzialmente una versione più lenta e personalizzata della soluzione "isnumerica" ​​offerta da @SilentGhost.
Greg,

@Greg: poiché @SilentGhost non copre correttamente i segni, questa versione funziona davvero.
S.Lott

1
@ S.Lott: sicuramente, chiunque sia in grado di pubblicare su SO, sarebbe in grado di estendere il mio esempio per coprire i segni.
SilentGhost,

2
le espressioni regolari riguardano la cosa più complessa e oscura esistente, trovo che il semplice controllo sopra sia sostanzialmente più chiaro, anche se penso che sia ancora brutto, questo è più brutto.
PlexQ,

18

La soluzione RegEx corretta combinerebbe le idee di Greg Hewgill e Nowell, ma non userebbe una variabile globale. È possibile ottenere ciò allegando un attributo al metodo. Inoltre, so che è disapprovato mettere le importazioni in un metodo, ma quello che sto cercando è un effetto "modulo pigro" come http://peak.telecommunity.com/DevCenter/Importing#lazy-imports

modifica: La mia tecnica preferita finora è usare esclusivamente metodi dell'oggetto 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))

E per i membri meno avventurosi della classe, ecco l'output:

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

4
Concordo che la mia suite di test è eccessiva. Mi piace dimostrare che il mio codice funziona quando lo scrivo. Ma pensi che la mia funzione isInteger sia eccessiva? Sicuramente no.
Bruno Bronosky,

1
Ho appena avuto un voto negativo senza commenti. Cosa c'è con le persone? Comprendo che i millennial ora usano "Mi piace" come "conferme di lettura". Ma ora usano i voti negativi come marcatori "non il metodo che ho scelto"? Forse non si rendono conto che sottrae 2 punti dalla TUA reputazione per votare una risposta. SO / SE lo fa per incoraggiare il voto contrario solo a causa di disinformazione, nel qual caso spero che lasceresti un commento .
Bruno Bronosky,

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

Quindi la tua funzione sarebbe:

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

1
is_int ("2") genera IndexError.
anttikoo,

4

L'approccio di Greg Hewgill mancava di alcuni componenti: il principale "^" per abbinare solo l'inizio della stringa e compilare in anticipo il re. Ma questo approccio ti permetterà di evitare un tentativo: exept:

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

Sarei interessato al motivo per cui stai cercando di evitare di provare: tranne?


1
Una questione di stile. Penso che "provare / tranne" dovrebbe essere usato solo con errori reali, non con il normale flusso di programma.
Adam Matan,

2
@Udi Pasmon: Python fa un uso abbastanza pesante di try / tranne che per un flusso di programma "normale". Ad esempio, ogni iteratore si interrompe con un'eccezione sollevata.
S.Lott

3
-1: Sebbene il tuo suggerimento per la compilazione della regex sia giusto, ti sbagli a criticare Greg sotto l'altro aspetto: re.match corrisponde all'inizio della stringa, quindi il ^ nel modello è effettivamente ridondante. (Questo è diverso quando si utilizza re.search).
ThomasH,

S.Lott - Questo è considerato un flusso ragionevole in Python? In che modo differisce dalle altre lingue? Forse vale una domanda separata.
Adam Matan,

1
L'uso pesante di Python di try / tranne è stato trattato qui su SO. Prova a cercare "[python] tranne"
S.Lott

4

Devo farlo tutto il tempo, e ho una leggera, ma dichiaratamente irrazionale, intenzione di usare il modello try / tranne. Io lo uso questo:

all([xi in '1234567890' for xi in x])

Non contiene numeri negativi, quindi è possibile eliminare un segno meno (se presente), quindi verificare se il risultato comprende cifre comprese tra 0 e 9:

all([xi in '1234567890' for xi in x.replace('-', '', 1)])

Puoi anche passare x a str () se non sei sicuro che l'input sia una stringa:

all([xi in '1234567890' for xi in str(x).replace('-', '', 1)])

Ci sono almeno due casi (limite?) In cui questo cade a pezzi:

  1. Non funziona per varie notazioni scientifiche e / o esponenziali (ad es. 1.2E3, 10 ^ 3, ecc.) - entrambi restituiranno False. Non credo che le altre risposte alloggiati questa operazione, e anche Python 3.8 ha opinioni incoerenti, in quanto type(1E2)<class 'float'>, mentre type(10^2)<class 'int'>.
  2. Un input stringa vuoto fornisce True.

Quindi non funzionerà per ogni possibile input, ma se è possibile escludere notazione scientifica, notazione esponenziale e stringhe vuote, è un controllo su una riga OK che restituisce Falsese x non è un numero intero e Truese x è un numero intero.

Non so se sia pythonic, ma è una riga ed è relativamente chiaro cosa fa il codice.


Prova / tranne sembra camminare sul prato di qualcuno (prova) e poi se / quando notano e si arrabbiano (eccezione) ti scusi (gestisci l'eccezione), mentre il mio all(xi in '1234567890' for xi in x])modello sembra più come chiedere il permesso di attraversare il prato. Non sono elettrizzato all'idea di chiedere l'autorizzazione, ma eccoci qui.
mRotten

3

penso

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

sarebbe meglio riscrivere a:

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

poiché s [1:] crea anche una nuova stringa

Ma la soluzione molto migliore è

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

3
Indovina cosa replacefa? Inoltre, questo accetterà in modo errato 5-2, ad esempio.
Ry-

Lancerà un IndexError ses='-'
Anti Earth

s = '-'; s.replace ('-', '') .isdigit () -> False
Vladyslav Savchenko

2

Mi è piaciuto molto il post di Shavais, ma ho aggiunto un altro test case (e la funzione integrata 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()

e batte significativamente i tempi del resto in modo significativo:

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

usando il normale 2.7 python:

$ python --version
Python 2.7.10

Entrambi i due casi di test che ho aggiunto (isInt_loop e isInt_digit) superano gli stessi casi di test esatti (entrambi accettano solo numeri interi senza segno), ma ho pensato che le persone potrebbero essere più intelligenti nel modificare l'implementazione della stringa (isInt_loop) al contrario dell'isdigit integrato (), quindi l'ho inclusa, anche se c'è una leggera differenza nei tempi di esecuzione. (ed entrambi i metodi battono tutto il resto di molto, ma non gestiscono le cose extra: "./+/-")

Inoltre, ho trovato interessante notare che il regex (metodo isInt_re2) ha battuto il confronto delle stringhe nello stesso test eseguito da Shavais nel 2012 (attualmente 2018). Forse le librerie regex sono state migliorate?


1

Questo è probabilmente il modo più semplice e pitonico per affrontarlo secondo me. Non ho visto questa soluzione ed è sostanzialmente la stessa di regex, ma senza regex.

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

set(input_string) == set(string.digits)se '-+ 'saltiamo all'inizio e .0, E-1alla fine.
jfs,

1

Ecco una funzione che analizza senza generare errori. Gestisce i casi ovvi restituisce Nonein caso di errore (gestisce fino a 2000 segni "- / +" per impostazione predefinita su 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)

Alcuni test:

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)))

risultati:

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

Per le tue esigenze puoi utilizzare:

def int_predicate(number):
     return get_int(number) is not None

1

Suggerisco quanto segue:

import ast

def is_int(s):
    return isinstance(ast.literal_eval(s), int)

Dai documenti :

Valuta in modo sicuro un nodo di espressione o una stringa contenente un display letterale o contenitore Python. La stringa o il nodo forniti possono essere costituiti solo dalle seguenti strutture letterali Python: stringhe, byte, numeri, tuple, elenchi, dadi, set, valori booleani e Nessuno.

Dovrei notare che questo solleverà ValueErrorun'eccezione quando chiamato contro qualsiasi cosa che non costituisca un letterale Python. Poiché la domanda ha richiesto una soluzione senza provare / tranne, ho una soluzione di tipo Kobayashi-Maru per questo:

from ast import literal_eval
from contextlib import suppress

def is_int(s):
    with suppress(ValueError):
        return isinstance(literal_eval(s), int)
    return False

¯ \ _ (ツ) _ / ¯


0

Ho una possibilità che non usa affatto int e non dovrebbe sollevare un'eccezione a meno che la stringa non rappresenti un numero

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

Dovrebbe funzionare per qualsiasi tipo di stringa che float accetta, positivo, negativo, notazione ingegneristica ...


0

Immagino che la domanda sia correlata alla velocità poiché il tentativo / tranne ha una penalità di tempo:

 dati di test

Innanzitutto, ho creato un elenco di 200 stringhe, 100 stringhe non riuscite e 100 stringhe numeriche.

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

 soluzione numpy (funziona solo con array e unicode)

np.core.defchararray.isnumeric può anche funzionare con stringhe unicode np.core.defchararray.isnumeric(u'+12')ma restituisce e array. Quindi, è una buona soluzione se devi fare migliaia di conversioni e hai dati mancanti o dati non numerici.

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

try / except

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

Sembra che la soluzione insensibile sia molto più veloce.


0

Se si desidera accettare solo cifre inferiori a ASCII, ecco i test per farlo:

Python 3.7+: (u.isdecimal() and u.isascii())

Python <= 3.6: (u.isdecimal() and u == str(int(u)))

Altre risposte suggeriscono di usare .isdigit()o .isdecimal()ma entrambe includono alcuni caratteri unicode superiori come '٢'( u'\u0662'):

u = u'\u0662'     # '٢'
u.isdigit()       # True
u.isdecimal()     # True
u.isascii()       # False (Python 3.7+ only)
u == str(int(u))  # False

Questo non gestirà i valori negativi o i valori imbottiti a spaziatura bianca, entrambi gestiti correttamente int().
ShadowRanger

-6

Uh .. Prova questo:

def int_check(a):
    if int(a) == a:
        return True
    else:
        return False

Funziona se non inserisci una stringa che non è un numero.

Inoltre (ho dimenticato di mettere la parte di controllo del numero), c'è una funzione che controlla se la stringa è un numero o no. È str.isdigit (). Ecco un esempio:

a = 2
a.isdigit()

Se chiami a.isdigit (), restituirà True.


Penso che tu abbia bisogno di preventivi intorno al valore 2assegnato a a.
Luke Woodward,

1
Perché questa non è la risposta migliore? Risponde esattamente alla domanda.
cavalletta

6
-1 la domanda: "Controlla se una stringa rappresenta un int, senza usare Try / Except?" per @Caroline Alexiou
jfs,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.