Как проверить, представляет ли строка целое число, без использования try/except? [аналогичный вопрос]
Проблема: Как проверить, представляет ли строка целое число?
Здравствуйте! У меня возник вопрос по поводу проверки, является ли строка представлением целого числа (например, '3'
, '-17'
), и важно, чтобы это не касалось чисел с плавающей запятой (таких как '3.14'
) или строк, не являющихся числами (например, 'asfasfas'
). Я хочу избежать использования механизма try/except для этой проверки.
Вот пример функции, которую я хотел бы реализовать:
is_int('3.14') == False
is_int('-7') == True
Есть ли какой-либо способ выполнить эту проверку без использования исключений? Заранее спасибо за помощь!
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 считает целыми числами, потребуется написать значительно больше кода. Я советую поступать по-питонистски в этом вопросе.
Я заметил, что 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
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
Для проверки строки на соответствие целому числу с использованием регулярного выражения, можно использовать следующий код:
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
Таким образом, вы сможете избежать повторной компиляции регулярного выражения на каждой итерации, что значительно ускорит выполнение программы.
Ваш код использует метод 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
.
Преобразование всех строк в списке в целые числа
Есть ли в Python метод подстроки 'contains' для строк?
Объединение двух столбцов текста в DataFrame pandas
Вывод строки в текстовый файл
Как извлечь числа из строки в Python?