Как я могу проверить, представляет ли строка int, без использования try / исключением?

467

Есть ли способ сказать, представляет ли строка целое число (например '3', '-17'но не '3.14'или 'asfasfas'), не используя механизм try / исключением?

is_int('3.14') = False
is_int('-7')   = True
Адам Матан
источник
23
Почему оба пытаются сделать это "трудным путем"? Что не так с try / кроме?
S.Lott
5
Да, что не так с try / кроме? Лучше просить прощения, чем разрешения.
mk12
53
Я бы спросил, почему эта простая вещь требует попробовать / кроме? Система исключений - сложный зверь, но это простая проблема.
Айвар
13
@ Айвар прекрати распространять ФУД. Один блок try / кроме не подходит даже к «сложному».
Триптих
47
Хотя это не совсем ФУД. Вы бы эффективно написали 4 строки кода, ожидая, что что-то взорвется, поймав это исключение и выполнив заданный по умолчанию вариант, вместо использования одной строки.
Андерсонвом

Ответы:

398

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

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

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

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

Триптих
источник
124
Таким образом, это просто для решения простой проблемы со сложным механизмом? Существует алгоритм для обнаружения записанной внутри функции int "int" - я не понимаю, почему это не отображается как булева функция.
Айвар
79
@Aivar: эта пятистрочная функция не является сложным механизмом.
Триптих
34
За исключением:>>> print RepresentsInt(10.0) True >>> print RepresentsInt(10.06) True
Даннид
5
Я думаю, что это «pythonic» в том смысле, что если Python считает, что строка является int, то и ваша программа тоже. Если изменяется Python, то же происходит и с вашей программой без изменения одной строки кода. В этом есть какая-то ценность. Это может быть правильным, в зависимости от обстоятельств.
Shavais
57
Я не знаю, почему это принятый ответ или у него так много откликов, так как это прямо противоположно тому, о чем просит OP.
FearlessFuture
757

с положительными целыми числами вы можете использовать .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()
SilentGhost
источник
6
это не обрабатывает "+17" без дополнительного специального случая.
Брайан Оукли
1
Вы должны проверить оба случая: лямбда-ы: s.isdigit () или (s.startswith ('-') и s [1:]. Isdigit ())
ограбить
4
@ Роберто: конечно, ты должен! и я уверен, что ты способен на это!
SilentGhost
22
примечание: u'²'.isdigit()верно, но int(u'²')вызывает ValueError. Используйте u.isdecimal()вместо этого. str.isdigit()зависит от языка Python 2.
JFS
4
check_int('')будет False
выдавать
97

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

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

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

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' в качестве тестового значения.

Мне очень интересно отметить, что на этом этапе ни одна из протестированных функций, включая метод 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 и так далее. Но это также возвращает истину для «06», так что. Выбери свой яд, наверное.

Shavais
источник
Возможно, отчасти это связано с тем, что целое число само по себе немного произвольно. Система программирования не может позволить себе роскошь предполагать, что она всегда будет десятичным представлением. 0x4df, является допустимым целым числом в некоторых местах, а 0891 нет в других. Я боюсь думать, что может возникнуть при использовании юникода в таких проверках.
PlexQ
3
+1 за время. Я согласен, что весь этот бизнес исключений не очень элегантен для такого простого вопроса. Вы ожидаете встроенный вспомогательный метод для такой распространенной проблемы ...
RickyA
9
Я знаю, что этот поток в основном неактивен, но +1 за рассмотрение времени выполнения. Длина линии не всегда указывает на сложность; и конечно, попробовать / за исключением может выглядеть простой (и читать легко, что тоже немаловажно), но это является дорогостоящей операцией. Я бы сказал, что иерархия предпочтений всегда должна выглядеть примерно так: 1. Легко читаемое явное решение (SilentGhost's). 2. Легко читаемое неявное решение (триптиха). 3. Нет трех.
Эрик Хамфри
1
Спасибо за ваши исследования по такой, казалось бы, незначительной теме. Я пойду с isInt_str (), pythonic или нет. Меня беспокоит то, что я ничего не понял о значении v.find ('..'). Это какой-то особый синтаксис поиска или крайний случай числовой строки?
JackLeEmmerdeur
3
Да, немного устаревший, но все еще очень хороший и актуальный анализ. В Python 3.5 tryэто более эффективно: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
Дейв
40

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
Catbuilts
источник
1
Потому что -23 дает ложь.
Баз
1
@ BuzzMoschetti ты прав. Быстрый способ исправить это удалить знак минуса с помощью re.replace (regex_search, regex_replace, content) перед применением str.isdigit ()
Catbuilts
27

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

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

Грег Хьюгилл
источник
19
+1: показывает, что это ужасно сложно и дорого по сравнению с попыткой / исключением.
S.Lott
2
Я чувствую, что это, по сути, более медленная, нестандартная версия решения «isnumeric», предлагаемого @SilentGhost.
Грег
@Greg: Поскольку @SilentGhost не покрывает знаки правильно, эта версия действительно работает.
S.Lott
1
@ S.Lott: конечно, любой, кто сможет разместить на SO, сможет расширить мой пример, чтобы охватить знаки.
SilentGhost
2
регулярные выражения - это самая сложная и непонятная вещь в мире, и я обнаружил, что простая проверка, приведенная выше, существенно более понятна, даже если я думаю, что она по-прежнему уродлива, это уродливее.
PlexQ
18

Правильное решение 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
Бруно Броноски
источник
4
Я согласен, что мой набор тестов излишний. Мне нравится доказывать, что мой код работает, когда я его пишу. Но вы думаете, моя функция isInteger излишня? Конечно нет.
Бруно Броноски
1
Я только что получил отрицательный голос без комментариев. Что с людьми? Я понимаю, что миллениалы теперь используют «лайки» как «чеки». Но используют ли они теперь отрицательные голоса в качестве маркеров «не тот метод, который я выбрал»? Возможно, они не понимают, что это вычитает 2 балла из ВАШЕЙ СОБСТВЕННОЙ репутации, чтобы понизить голос за ответ. SO / SE делает это для того, чтобы поощрить голосование только из-за дезинформации, и в этом случае я надеюсь, что вы оставите комментарий .
Бруно Броноски
5
>>> "+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()
alkos333
источник
1
is_int ("2") вызывает IndexError.
anttikoo
4

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

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

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

Новелл
источник
1
Вопрос стиля. Я думаю, что «попробовать / кроме» следует использовать только с фактическими ошибками, а не с нормальным потоком программ.
Адам Матан
2
@Udi Pasmon: Python довольно интенсивно использует try /, за исключением «нормального» потока программ. Например, каждый итератор останавливается с повышенным исключением.
S.Lott
3
-1: Хотя ваш совет по компиляции регулярного выражения верен, вы ошибаетесь в критике Грега в другом отношении: re.match сопоставляется с началом строки, поэтому символ ^ в шаблоне фактически избыточен. (Это отличается, когда вы используете re.search).
ThomasH
С.Лотт - Это считается разумным потоком в питоне? Чем это отличается от других языков? Возможно, стоит отдельный вопрос.
Адам Матан
1
Интенсивное использование в Python try / кроме было рассмотрено здесь на SO. Попробуйте найти «[python] кроме»
S.Lott
4

Я должен делать это все время, и у меня есть легкое, но, по общему признанию, иррациональное отвращение к использованию шаблона «попробуй / кроме». Я использую это:

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

Он не содержит отрицательных чисел, поэтому вы можете удалить один знак минус (если есть), а затем проверить, содержит ли результат цифры от 0 до 9:

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

Вы также можете передать x в str (), если вы не уверены, что ввод является строкой:

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

Есть по крайней мере два (край?) Случая, когда это разваливается:

  1. Он не работает для различных научных и / или экспоненциальных обозначений (например, 1.2E3, 10 ^ 3 и т. Д.) - оба возвращают False. Я не думаю, что другие ответы также соответствовали этому, и даже Python 3.8 имеет противоречивые мнения, поскольку type(1E2)дает, <class 'float'>тогда как type(10^2)дает <class 'int'>.
  2. Ввод пустой строки дает True.

Таким образом, он не будет работать для всех возможных входных данных, но если вы можете исключить научную запись, экспоненциальную запись и пустые строки, то это будет однострочная проверка ОК, которая возвращает, Falseесли x не является целым числом и Trueесли x является целым числом.

Я не знаю, является ли это pythonic, но это одна строка, и это относительно ясно, что делает код.

mRotten
источник
Попытка / исключение выглядит как ходьба по чьей-то лужайке (попытка), а затем, если / когда они замечают и злятся (исключение), вы приносите свои извинения (обрабатываете исключение), тогда как мой all(xi in '1234567890' for xi in x])шаблон больше похож на просьбу разрешить пройти по лужайке. Я не в восторге от того, что спрашиваю разрешения, но мы здесь.
mRotten
3

Я думаю

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

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

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

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

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

s.lstrip('+-').isdigit()
Владислав Савченко
источник
3
Угадай, что replaceделает? Кроме того, это будет неправильно принимать 5-2, например.
Ry-
Выкинет IndexError, еслиs='-'
Anti Earth
s = '-'; s.replace ('-', '') .isdigit () -> False
Владислав Савченко
2

Мне очень понравился пост Шавайса, но я добавил еще один тестовый пример (и встроенную функцию 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 (), поэтому я включил ее, хотя есть небольшая разница во времени выполнения. (и оба метода намного превосходят все остальное, но не обрабатывают лишние вещи: "./+/-")

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

brw59
источник
1

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

def is_int(test):
    import string
    return not (set(test) - set(string.digits))
Xenlyte
источник
set(input_string) == set(string.digits)если мы пропускаем '-+ 'в начале и .0, E-1в конце концов.
Jfs
1

Вот функция, которая анализирует без возникновения ошибок. Он обрабатывает очевидные случаи возврата Noneпри сбое (по умолчанию обрабатывает до 2000 знаков '- / +' на 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)

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

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
Реут Шарабани
источник
1

Я предлагаю следующее:

import ast

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

Из документов :

Безопасно оцените узел выражения или строку, содержащую литерал Python или отображение контейнера. Предоставленная строка или узел могут состоять только из следующих литеральных структур Python: строк, байтов, чисел, кортежей, списков, диктов, множеств, логических значений и None.

Я должен отметить, что это вызовет ValueErrorисключение при вызове против всего, что не является литералом Python. Поскольку вопрос задан для решения без попытки / кроме, у меня есть решение типа Кобаяси-Мару для этого:

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

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

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

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

agomcas
источник
0

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

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

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

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 намного быстрее.

Карлос Вега
источник
0

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

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

Python <= 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
krubo
источник
Это не будет обрабатывать отрицательные значения или дополненные пробелами значения, оба из которых обрабатываются очень хорошо int().
ShadowRanger
-6

Э-э .. Попробуйте это:

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

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

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

a = 2
a.isdigit()

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

HaulCozen
источник
Я думаю, что вам нужны кавычки вокруг значения, 2присвоенного a.
Люк Вудворд
1
Почему это не лучший ответ? Это точно отвечает на вопрос.
кузнечик
6
-1 вопрос: «Проверить, представляет ли строка целое число, не используя Try / Except?» для @Caroline Alexiou
JFS