有没有办法告诉一个字符串是否代表一个整数(例如,'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执行的相同测试中击败了字符串比较。也许正则表达式库已经改进了?

其他回答

据我所知,你想检查字符串可转换的int。要做到这一点你可以:

将'-'替换为空,因为'-'不是数字和'-7'也可以转换为int。 检查一下是不是数字。

def is_string_convertable_to_int(value: str) -> bool:
    return value.replace('-', '').isdigit()

另外,你可以很容易地修改这个def来检查字符串在float中的可转换性,只需添加replace('。', "),并检查一个'。'使用value.count('.') = 1存在。

对于正整数,可以使用.isdigit:

>>> '16'.isdigit()
True

但它对负整数不起作用。假设您可以尝试以下方法:

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

它不适用于'16.0'格式,在这个意义上,'16.0'格式类似于int类型强制转换。

编辑:

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

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

我的建议如下:

import ast

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

从文档中可以看出:

安全地计算表达式节点或包含Python文字或容器显示的字符串。提供的字符串或节点只能由以下Python文字结构组成:字符串、字节、数字、元组、列表、字典、集、布尔值和None。

我应该指出,当对任何不构成Python文字的内容调用时,这将引发ValueError异常。因为这个问题要求一个没有try/except的解决方案,我有一个Kobayashi-Maru类型的解决方案:

from ast import literal_eval
from contextlib import suppress

def is_int(s):
    with suppress(ValueError):
        return isinstance(literal_eval(s), int)
    return False

¯\_(五)_/¯

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,所以。我想,选择你的毒药吧。