Как проверить, соответствует ли строка int, не используя try/except?

Можно ли указать, является ли строка целым числом (например, '3', '-17', но не '3.14' или 'asfasfas')) Без использования механизма try/except?

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

Ответ 1

Если вы действительно раздражены при использовании try/except повсюду, просто напишите вспомогательную функцию:

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

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

Это будет ПУТЬ больше кода, чтобы точно покрыть все строки, которые Python считает целыми числами. Я говорю только о том, чтобы быть питоническим на этом.

Ответ 2

с положительными целыми числами вы можете использовать .isdigit:

>>> '16'.isdigit()
True

он не работает с отрицательными целыми числами. предположим, что вы можете попробовать следующее:

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

он не будет работать с форматом '16.0', который похож на кастинг int в этом смысле.

изменить

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

Ответ 3

Вы знаете, я нашел (и я тестировал это снова и снова), которые пытаются/за исключением того, что не выполняют все это по какой-либо причине. Я часто пытаюсь несколько способов сделать что-то, и я не думаю, что когда-либо нашел метод, который использует try/except для выполнения лучших из тестируемых, на самом деле мне кажется, что эти методы обычно выходят близко к худшее, если не самое худшее. Не во всех случаях, а во многих случаях. Я знаю, что многие люди говорят, что это "питонический" способ, но это одна область, где я расстаюсь с ними. Для меня это не очень показательно и не очень элегантно, поэтому я стараюсь использовать его только для захвата ошибок и отчетности.

Я собирался понять, что PHP, perl, ruby, C и даже freaking shell имеют простые функции для тестирования строки для целочисленного капота, но должная осмотрительность в проверке того, что эти предположения подстегнула меня! Видимо, это недостаток - обычная болезнь.

Здесь быстрое и грязное редактирование сообщения Бруно:

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

Вот результаты сравнения производительности:

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

Метод AC может сканировать его один раз через, и будет сделано. По-моему, метод AC, который сканирует строку один раз, будет правильным делом.

РЕДАКТИРОВАТЬ:

Я обновил приведенный выше код для работы в Python 3.5 и включил функцию check_int из текущего наиболее голосового ответа и использовал текущее самое популярное регулярное выражение, которое можно найти для тестирования для целочисленного капота. Это регулярное выражение отклоняет строки типа 'abc 123'. Я добавил "abc 123" в качестве тестового значения.

Мне очень интересно отметить, что на данный момент NONE проверенных функций, включая метод try, популярную функцию check_int и самое популярное регулярное выражение для тестирования для целочисленного капота, вернет правильные ответы для всех тестовые значения (ну, в зависимости от того, что вы считаете правильными ответами, см. результаты теста ниже).

Встроенная функция int() молча усекает дробную часть числа с плавающей запятой и возвращает целочисленную часть перед десятичной точкой, если только число с плавающей запятой сначала не преобразуется в строку.

Функция check_int() возвращает false для значений, таких как 0.0 и 1.0 (которые технически являются целыми числами) и возвращает true для значений типа "06".

Вот текущие результаты теста (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   |

Только сейчас я попытался добавить эту функцию:

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

Он выполняет почти так же, как check_int (0.3486), и возвращает true для значений типа 1.0 и 0.0 и +1.0 и 0. и.0 и так далее. Но он также возвращает true для '06', так что. Выбери свой яд, я думаю.

Ответ 4

Используйте регулярное выражение:

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

Если вы также должны принимать десятичные дроби:

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

Для повышения производительности, если вы часто это делаете, скомпилируйте регулярное выражение только один раз, используя re.compile().

Ответ 5

str.isdigit() должен сделать str.isdigit() дело.

Примеры:

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

РЕДАКТИРОВАТЬ: Как @BuzzMoschetti указал, этот путь не будет работать для минус число (например, "-23"). Если ваш input_num может быть меньше 0, используйте re.sub(regex_search, regex_replace, contents) перед применением str.isdigit(). Например:

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

Ответ 6

Правильное решение RegEx объединит идеи Грега Хьюджилла и Новелла, но не использует глобальную переменную. Вы можете выполнить это, присоединив атрибут к методу. Кроме того, я знаю, что он не согласен вносить импорт в метод, но то, что я собираюсь, это эффект "ленивого модуля", например http://peak.telecommunity.com/DevCenter/Importing#lazy-imports

edit: Моя любимая техника до сих пор заключается в использовании исключительно методов объекта 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))

И для менее предприимчивых членов класса, вот вывод:

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

В подходе Грега Хьюджилла отсутствовали несколько компонентов: ведущий "^", чтобы соответствовать началу строки, и компилировать повтор заранее. Но этот подход позволит вам избежать попытки: кроме:

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

Мне было бы интересно, почему вы пытаетесь избежать try: except?

Ответ 8

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

Таким образом, ваша функция будет:

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

Ответ 9

Я думаю,

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

лучше переписать на:

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

потому что s [1:] также создает новую строку

Но гораздо лучшее решение -

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

Ответ 10

Мне очень понравилось сообщение Shavais, но я добавил еще один тестовый пример (и встроенная функция 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()

и он значительно последовательно превосходит время остальных:

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

используя обычный 2,7-питон:

$ python --version
Python 2.7.10

Оба двух тестовых примера, которые я добавил (isInt_loop и isInt_digit), передают одни и те же тестовые примеры (оба они принимают только целые числа без знака), но я думал, что люди могут быть более умными с модификацией реализации строки (isInt_loop), противоположной встроенной isdigit(), поэтому я включил его, несмотря на небольшую разницу во времени выполнения. (и оба метода сильно избили все остальное, но не обрабатывают дополнительный материал: "./+ / -")

Кроме того, мне было интересно заметить, что метод regex (isInt_re2) превзошел сравнение строк в том же тесте, который был выполнен Shavais в 2012 году (в настоящее время 2018). Возможно, библиотеки регулярных выражений были улучшены?

Ответ 11

Это, пожалуй, самый простой и питонический подход к моему мнению. Я не видел этого решения, и это в основном то же самое, что и регулярное выражение, но без регулярного выражения.

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

Ответ 12

Вот функция, которая анализирует без ошибок. Он обрабатывает очевидные случаи, возвращает None при сбое (по умолчанию на CPython обрабатывает знаки до 2000 "-/+" ):

#!/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)

Некоторые тесты:

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

Результаты:

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

Для ваших нужд вы можете использовать:

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

Ответ 13

У меня есть одна возможность, которая не использует int вообще, и не должна поднимать исключение, если строка не представляет число

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

Он должен работать для любой строки, которая принимает float, положительную, отрицательную, техническую нотацию...

Ответ 14

Я предполагаю, что вопрос связан со скоростью, так как попытка/исключение имеет штраф за время:

данные испытаний

Сначала я создал список из 200 строк, 100 строк с ошибками и 100 строк с цифрами.

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

решение NumPy (работает только с массивами и Unicode)

np.core.defchararray.isnumeric также может работать со строками Unicode np.core.defchararray.isnumeric(u'+12') но возвращает и массив. Таким образом, это хорошее решение, если вам нужно выполнить тысячи преобразований и у вас отсутствуют данные или не числовые данные.

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

Кажется, что решение NumPy намного быстрее.

Ответ 15

Если вы хотите принимать только младшие цифры, вот тесты для этого:

Python 3. 7+: (u.isdecimal() and u.isascii())

Python & lt; = 3.6: (u.isdecimal() and u == str(int(u)))

В других ответах предлагается использовать .isdigit() или .isdecimal(), но оба содержат символы верхнего юникода, например '٢' (u'\u0662'):

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

Ответ 16

Ух.. Попробуйте следующее:

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

Это работает, если вы не поместите строку, а не число.

А также (я забыл поставить часть проверки числа.), есть функция, проверяющая, является ли строка числом или нет. Это str.isdigit(). Вот пример:

a = 2
a.isdigit()

Если вы вызываете a.isdigit(), он вернет True.