有没有办法告诉一个字符串是否代表一个整数(例如,'3','-17'但不是'3.14'或'asfasfas')而不使用try/except机制?

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

当前回答

我真的很喜欢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:

$ python --version
Python 2.7.10

我添加的两个测试用例(isInt_loop和isInt_digit)都通过了完全相同的测试用例(它们都只接受无符号整数),但我认为人们可以更聪明地修改字符串实现(isInt_loop)而不是内置的isdigit()函数,所以我包括了它,尽管执行时间略有不同。(这两种方法都比其他方法好很多,但没有处理额外的东西:“。/ + / -)

此外,我发现有趣的是,regex (isInt_re2方法)在2012年(目前是2018年)由Shavais执行的相同测试中击败了字符串比较。也许正则表达式库已经改进了?

其他回答

我用的最简单的方法

def is_int(item: str) -> bool:
    return item.lstrip('-+').isdigit()

You know, I've found (and I've tested this over and over) that try/except does not perform all that well, for whatever reason. I frequently try several ways of doing things, and I don't think I've ever found a method that uses try/except to perform the best of those tested, in fact it seems to me those methods have usually come out close to the worst, if not the worst. Not in every case, but in many cases. I know a lot of people say it's the "Pythonic" way, but that's one area where I part ways with them. To me, it's neither very performant nor very elegant, so, I tend to only use it for error trapping and reporting.

我本来想抱怨PHP, perl, ruby, C,甚至是该死的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

C语言的方法可以对它进行一次扫描。我认为,用C语言来扫描字符串是正确的做法。

编辑:

我更新了上面的代码,使其能够在Python 3.5中工作,并包含了来自当前投票最多的答案的check_int函数,并使用了我能找到的当前最流行的正则表达式来测试整型。这个正则表达式拒绝'abc 123'这样的字符串。我添加了'abc 123'作为测试值。

我很有趣地注意到,在这一点上,测试的所有函数,包括try方法、流行的check_int函数和最流行的正则表达式,都没有返回所有测试值的正确答案(好吧,这取决于你认为的正确答案是什么;请参阅下面的测试结果)。

内置的int()函数无声地截断浮点数的小数部分并返回小数之前的整数部分,除非浮点数首先转换为字符串。

check_int()函数对于0.0和1.0(技术上是整数)这样的值返回false,对于'06'这样的值返回true。

以下是当前(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)一样好,对于1.0和0.0以及+1.0和0这样的值,它返回true。0等等。但它也为'06'返回true,所以。我想,选择你的毒药吧。

先决条件:

我们谈论的是整数(不是小数/浮点数); 内置int()的行为是我们的标准(有时很奇怪:“-00”是它的正确输入)

简短的回答:

使用下面的代码。它简单,正确(虽然这个线程中的许多变体不是),并且几乎是try/except和regex变体的两倍。

def is_int_str(string):
    return (
        string.startswith(('-', '+')) and string[1:].isdigit()
    ) or string.isdigit()

TL;博士答:

我已经测试了3个主要变体(1)try/except, (2) re.match()和(3)字符串操作(见上文)。第三个变体比try/except和re.match()快两倍。顺便说一句:regex变体是最慢的!请参见下面的测试脚本。

import re
import time


def test(func, test_suite):
    for test_case in test_suite:
        actual_result = func(*test_case[0])
        expected_result = test_case[1]
        assert (
            actual_result == expected_result
        ), f'Expected: {expected_result} but actual: {actual_result}'


def perf(func, test_suite):
    start = time.time()

    for _ in range(0, 1_000_000):
        test(func, test_suite)

    return time.time() - start


def is_int_str_1(string):
    try:
        int(string)
        return True
    except ValueError:
        return False


def is_int_str_2(string):
    return re.match(r'^[\-+]?\d+$', string) is not None


def is_int_str_3(string):
    return (
        string.startswith(('-', '+')) and string[1:].isdigit()
    ) or string.isdigit()


# Behavior of built-in int() function is a standard for the following tests
test_suite = [
    [['1'], True],  # func('1') -> True
    [['-1'], True],
    [['+1'], True],
    [['--1'], False],
    [['++1'], False],
    [['001'], True],  # because int() can read it
    [['-00'], True],  # because of quite strange behavior of int()
    [['-'], False],
    [['abracadabra'], False],
    [['57938759283475928347592347598357098458405834957984755200000000'], True],
]

time_span_1 = perf(is_int_str_1, test_suite)
time_span_2 = perf(is_int_str_2, test_suite)
time_span_3 = perf(is_int_str_3, test_suite)

print(f'{is_int_str_1.__name__}: {time_span_1} seconds')
print(f'{is_int_str_2.__name__}: {time_span_2} seconds')
print(f'{is_int_str_3.__name__}: {time_span_3} seconds')

输出是:

is_int_str_1: 4.314162969589233 seconds
is_int_str_2: 5.7216269969940186 seconds
is_int_str_3: 2.5828163623809814 seconds

Str.isdigit()应该可以做到这一点。

例子:

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

编辑: 正如@BuzzMoschetti指出的那样,这种方法对于负数(例如“-23”)将失败。如果您的input_num可以小于0,请在应用str.isdigit()之前使用re.sub(regex_search,regex_replace,contents)。例如:

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

如果你真的不喜欢到处使用try/except,请写一个helper函数:

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认为是整数的所有字符串。要我说,你就用蟒语吧。