如何检查字符串是否代表int,而不使用try/except?

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

is_int('3.14') == False
is_int('-7')   == True
817313 次浏览

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

>>> '16'.isdigit()
True

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

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

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

# EYZ0:

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

使用正则表达式:

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()只编译正则表达式一次。

Greg Hewgill的方法缺少了几个组件:前导的“^”只匹配字符串的开头,并且预先编译re。但是这种方法可以让你避免尝试:

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

我很感兴趣为什么你试图避免尝试:除了?

如果你真的讨厌到处使用try/excepts,请写一个帮助函数:

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


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

它将需要更多的代码来精确覆盖Python认为是整数的所有字符串。要我说,你就用蟒语吧。

正确的RegEx解决方案应该结合Greg Hewgill和Nowell的思想,但不使用全局变量。可以通过将属性附加到方法来实现这一点。另外,我知道在方法中导入是不受欢迎的,但我想要的是像http://peak.telecommunity.com/DevCenter/Importing#lazy-imports这样的“惰性模块”效果

到目前为止,我最喜欢的技术是使用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

呃. .试试这个:

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

如果你不输入非数字的字符串,这是可行的。

还有(我忘了写数字检查部分了。),有一个函数检查字符串是否是数字。它是str.isdigit()。这里有一个例子:

a = 2
a.isdigit()

如果调用a.s isdigit(),它将返回True。

你知道,我已经发现(我已经测试了一遍又一遍),try/except无论出于什么原因都不能很好地执行。我经常尝试几种方法来做事情,我认为我从来没有找到一个使用try/except的方法来执行那些测试中最好的,事实上,在我看来,这些方法通常都是最糟糕的,如果不是最糟糕的。不是每一种情况,但在很多情况下。我知道很多人说它是“python”。但这是我和他们意见相左的一个方面。对我来说,它既不太高性能,也不太优雅,因此,我倾向于只将它用于错误捕获和报告。

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

>>> "+7".lstrip("-+").isdigit()
True
>>> "-7".lstrip("-+").isdigit()
True
>>> "7".lstrip("-+").isdigit()
True
>>> "13.4".lstrip("-+").isdigit()
False

所以你的函数是

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

在我看来,这可能是最直接和python化的方法。我没有看到这个解它基本上和正则表达式的解是一样的,但是没有正则表达式。

def is_int(test):
import string
return not (set(test) - set(string.digits))

我认为

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

最好重写为:

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

因为s[1:]也创建了一个新的字符串

但更好的解决办法是

s.lstrip('+-').isdigit()

我有一种可能根本不使用int,除非字符串不代表数字,否则不应该引发异常

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

它应该适用于任何类型的字符串,float接受,正,负,工程符号…

下面是一个解析时不会产生错误的函数。它处理明显的情况,失败时返回None(在CPython上默认处理最多2000个'-/+'符号!):

#!/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

我真的很喜欢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执行的相同测试中击败了字符串比较。也许正则表达式库已经改进了?

我猜这个问题与速度有关,因为try/except有一个时间惩罚:

测试数据

首先,我创建了一个包含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解决方案(仅适用于数组和unicode)

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解决方案似乎更快。

str.isdigit()应该可以做到。

例子:

str.isdigit("23") ## True
str.isdigit("abc") ## False
str.isdigit("23.4") ## False
< p > # EYZ0: 正如@BuzzMoschetti指出的那样,这种方法对于-数量(例如“-23”)将失败。如果你的input_num可以小于0,在应用str.isdigit ()之前使用re.sub (regex_search regex_replace,内容)。例如:

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

如果你想只接受低ascii码的数字,这里有这样做的测试:

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

Python <= 3.6: (u.isdecimal() and u == str(int(u)))

其他答案建议使用.isdigit().isdecimal(),但这些两者都包含一些上层unicode字符,如'٢' (u'\u0662'):

u = u'\u0662'     # '٢'
u.isdigit()       # True
u.isdecimal()     # True
u.isascii()       # False (Python 3.7+ only)
u == str(int(u))  # False

我的建议如下:

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

¯\_()_/¯

我一直这样做,我对使用try/except模式有一种温和但不可否认的非理性厌恶。我用这个:

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

它不包含负数,所以你可以去掉左边所有的负号,然后检查结果是否包含0-9之间的数字:

all([xi in '1234567890' for xi in x.lstrip('-')])

如果你不确定输入是字符串,你也可以将x传递给str():

all([xi in '1234567890' for xi in str(x).lstrip('-')])

有一些(边缘?)情况下,这是行不通的:

  1. 它不适用于各种科学和/或指数符号(例如1.2E3, 10^3等)-两者都会返回False。我认为其他答案也不符合这一点,甚至Python 3.8也有不一致的意见,因为type(1E2)给出了<class 'float'>,而type(10^2)给出了<class 'int'>
  2. 空字符串输入为True。
  3. 前面的加号(如"+7")表示False。
  4. 多个负号只要是主角就会被忽略。此行为类似于python解释器*,即type(---1)返回<class int>。然而,它与解释器不完全一致,因为int('---1')给出了一个错误,但我的解决方案返回了具有相同输入的True

所以它对每一个可能的输入无效,但如果你可以排除这些,它是一个OK的一行检查,如果x不是整数返回False,如果x是整数返回True。但是如果你真的想要完全模仿int()内置的行为,你最好使用try/except。

我不知道这是否是python式的,但它只有一行,而且代码的功能相对清晰。

我并不是说解释器忽略了前导负号,只是说任何数量的前导负号都不会改变结果是整数。int(--1)实际上被解释为-(-1)或1。int(---1)被解释为-(-(-1))或-1。所以前面有偶数个负号就会得到一个正整数,前面有奇数个负号就会得到一个负整数,但结果总是整数。

我用的最简单的方法

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

先决条件:

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

简短的回答:

使用下面的代码。它是简单的正确的(虽然这个线程中的许多变体都不是),而且几乎是两次优于try/exceptregex变体。

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/exceptre.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

可以用下面的方法进行检查。

def check_if_string_is_int(string1):
for character in string1:
if not character.isdigit():
return "Not a number"
else:
return "Is a number"
检查后将值转换为字符串,然后检查字符串的第一个字符值是-+,其余的字符串是isdigit。最后检查isdigit

# EYZ0 < / p >

检查

for k,v in enumerate(test):
print(k, v, 'test: ', True if isinstance(v, int) is not False else True if str(v)[0] in ['-', '+'] and str(v)[1:].isdigit() else str(v).isdigit())

结果

0 1 test:  True
1 12015 test:  True
2 1..2 test:  False
3 a2kk78 test:  False
4 1.5 test:  False
5 2 test:  True
6 1.24 test:  False
7 -8.5 test:  False
8 +88751.71 test:  False
9 -1 test:  True
10 +7 test:  True

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

  1. 将'-'替换为空,因为'-'不是数字和'-7'也可以转换为int。
  2. 检查一下是不是数字。
def is_string_convertable_to_int(value: str) -> bool:
return value.replace('-', '').isdigit()

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