7

Как проверить, представляет ли строка целое число, без использования try/except? [аналогичный вопрос]

25

Проблема: Как проверить, представляет ли строка целое число?

Здравствуйте! У меня возник вопрос по поводу проверки, является ли строка представлением целого числа (например, '3', '-17'), и важно, чтобы это не касалось чисел с плавающей запятой (таких как '3.14') или строк, не являющихся числами (например, 'asfasfas'). Я хочу избежать использования механизма try/except для этой проверки.

Вот пример функции, которую я хотел бы реализовать:

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

Есть ли какой-либо способ выполнить эту проверку без использования исключений? Заранее спасибо за помощь!

5 ответ(ов)

5

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

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

Пример использования:

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

На самом деле, чтобы точно обработать все строки, которые Python считает целыми числами, потребуется написать значительно больше кода. Я советую поступать по-питонистски в этом вопросе.

1

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

Я хотел пожаловаться на то, что в PHP, Perl, Ruby, C и даже в оболочке есть простые функции для проверки, является ли строка целым числом, но тщательная проверка этих предположений сбила меня с толку! Кажется, это распространенная проблема.

Вот быстрое и грязное редактирование поста Бруно:

import sys, time, re

g_intRegex = re.compile(r"^([+-]?[1-9]\d*|0)$")

testvals = [
    # целые числа
    0, 1, -1, 1.0, -1.0,
    '0', '0.','0.0', '1', '-1', '+1', '1.0', '-1.0', '+1.0', '06',
    # не целые числа
    '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'},
    # с пробелами
    ' 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):
    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 isinstance(v, str):
            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("тесты..")
    testFuncs((isInt_try, isInt_str, isInt_re, isInt_re2, check_int))
    print()

    print("временные замеры..")
    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))

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

временные замеры..
isInt_try:   0.6426
isInt_str:   0.7382
isInt_re:    1.1156
isInt_re2:   0.5344
check_int:   0.3452

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

UPDATE:

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

Мне очень интересно отметить, что НИКТО из протестированных функций, включая метод 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

0

str.isdigit() отлично подходит для этой задачи.

Примеры:

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

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

import re
input_num = "-23"
input_num = re.sub("^-", "", input_num) ## "^" указывает на удаление только первого "-"
str.isdigit(input_num) ## True
0

Для проверки строки на соответствие целому числу с использованием регулярного выражения, можно использовать следующий код:

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(). Вот как это можно сделать:

import re

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

Таким образом, вы сможете избежать повторной компиляции регулярного выражения на каждой итерации, что значительно ускорит выполнение программы.

0

Ваш код использует метод lstrip для удаления символов - и + из начала строки, а затем проверяет, является ли оставшаяся часть строки целым числом с помощью метода isdigit. Это действительно работает для строк, представляющих целые числа, но не подходит для строк, представляющих дробные числа.

Вот как ваш код работает:

>>> "+7".lstrip("-+").isdigit()
True  # '7' - целое число
>>> "-7".lstrip("-+").isdigit()
True  # '7' - целое число
>>> "7".lstrip("-+").isdigit()
True  # '7' - целое число
>>> "13.4".lstrip("-+").isdigit()
False  # '13.4' - не целое число

Таким образом, ваша функция is_int корректно определяет, является ли строка целым числом:

def is_int(val):
   return val.lstrip("-+").isdigit()

Имейте в виду, что это решение будет возвращать False для дробных чисел, как показано в примере с 13.4. Если вы хотите дополнительно проверять и дробные числа, вам нужно будет изменить логику проверки, например, используя try и except для преобразования строки в число типа int.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь