有没有办法在不使用 try/except 机制的情况下判断 string 是否表示整数(例如,'3'
、'-17'
但不是 '3.14'
或 'asfasfas'
)?
is_int('3.14') == False
is_int('-7') == True
对于正整数,您可以使用 .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()
如果您真的对到处使用 try/except
感到厌烦,请编写一个辅助函数:
def RepresentsInt(s):
try:
int(s)
return True
except ValueError:
return False
>>> print RepresentsInt("+123")
True
>>> print RepresentsInt("10.0")
False
这将是更多的代码来准确覆盖 Python 认为整数的所有字符串。我说这只是pythonic。
>>> print RepresentsInt(10.0)
True
>>> print RepresentsInt(10.06)
True
try/except
(即使包装在 func 中)。
你知道,我发现(并且我已经反复测试过)无论出于何种原因,try/except 的性能都不是很好。我经常尝试几种做事的方法,但我认为我从未找到使用 try/except 来执行测试中最好的方法,事实上在我看来这些方法通常接近最坏的,如果不是最坏的。并非在所有情况下,但在许多情况下。我知道很多人说这是“Pythonic”方式,但这是我与他们分道扬镳的一个领域。对我来说,它既不是很高效也不是很优雅,所以,我倾向于只将它用于错误捕获和报告。
我会抱怨 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
AC方法可以扫描一次,然后完成。我认为,扫描字符串一次的 AC 方法是正确的做法。
编辑:
我已经更新了上面的代码以在 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. 和 .0 等值返回 true。但它也为'06'返回true,所以。我猜,选择你的毒药。
try
更高效:isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168。
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
s.isnumeric()
有什么区别?
使用正则表达式:
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()
只编译一次正则表达式。
正确的 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
>>> "+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()
我一直这样做 b/c 我对使用 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.2E3、10^3 等)——两者都将返回 False。我认为其他答案也不适用,甚至 Python 3.8 也有不一致的意见,因为 type(1E2) 给出
因此,它不适用于 每个 可能的输入,但如果您可以排除这些输入,这是一个 OK 的单行检查,如果 x 不是整数则返回 False
,如果 x 是则返回 True
一个整数。但是,如果您真的想要 完全 模拟内置 int()
的行为,则最好使用 try/except。
不知道是不是pythonic,但就是一行,代码的作用也比较清楚。
*我并不是说解释器忽略前导减号,只是任何数量的前导减号都不会改变结果是整数。 int(--1)
实际上被解释为 -(-1)
或 1。int(---1)
被解释为 -(-(-1))
或 -1。所以偶数个前导减号给出一个正整数,奇数个负号给出一个负整数,但结果总是一个整数。
'12-34'
返回 True
。
.replace()
和 .lstrip()
实现都足以满足 OP 的示例。
Greg Hewgill 的方法缺少一些组件:前导“^”只匹配字符串的开头,并预先编译 re。但是这种方法可以让你避免尝试:exept:
import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
return INT_RE.match(str(s)) is not None
我很想知道你为什么要避免尝试:除了?
我使用的最简单的方法
def is_int(item: str) -> bool:
return item.lstrip('-+').isdigit()
-+123
为真
我认为
s.startswith('-') and s[1:].isdigit()
最好重写为:
s.replace('-', '').isdigit()
因为 s[1:] 也创建了一个新字符串
但更好的解决方案是
s.lstrip('+-').isdigit()
replace
做了什么?此外,例如,这将错误地接受 5-2
。
s='-'
将抛出 IndexError
s.lstrip('+-').isdigit()
接受 -+1
或 +++++++1
之类的内容
可以使用下面的方法来检查。
def check_if_string_is_int(string1):
for character in string1:
if not character.isdigit():
return "Not a number"
else:
return "Is a number"
我真的很喜欢 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)都通过了完全相同的测试用例(它们都只接受无符号整数),但我认为人们可以更聪明地修改与内置 isdigit 相对的字符串实现(isInt_loop) () 函数,所以我将它包括在内,即使执行时间略有不同。 (这两种方法都比其他方法好很多,但不要处理额外的东西: "./+/-" )
此外,我确实发现有趣的是,正则表达式(isInt_re2 方法)在 Shavais 在 2012 年(目前是 2018 年)执行的同一测试中击败了字符串比较。也许正则表达式库已经改进?
在我看来,这可能是最直接和最pythonic的方法。我没有看到这个解决方案,它与正则表达式基本相同,但没有正则表达式。
def is_int(test):
import string
return not (set(test) - set(string.digits))
set(input_string) == set(string.digits)
如果我们在开头跳过 '-+ '
并在结尾跳过 .0
、E-1
。
这是一个解析而不会引发错误的函数。它处理失败时返回 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
如果您只想接受较低的 ascii 数字,请执行以下测试:
Python 3.7+:(u.isdecimal() and u.isascii())
Python <= 3.6:(u.isdecimal() and u == str(int(u)))
其他答案建议使用 .isdigit()
或 .isdecimal()
,但这些 both include some upper-unicode characters 例如 '٢'
(u'\u0662'
):
u = u'\u0662' # '٢'
u.isdigit() # True
u.isdecimal() # True
u.isascii() # False (Python 3.7+ only)
u == str(int(u)) # False
int()
可以很好地处理这两个值。
前提条件:
我们谈论的是整数(不是小数/浮点数);
内置 int() 的行为对我们来说是一个标准(有时很奇怪:“-00”是它的正确输入)
简短的回答:
使用以下代码。它是简单、正确(虽然此线程中的许多变体不是)并且几乎两次优于 try/except
和 regex
变体.
def is_int_str(string):
return (
string.startswith(('-', '+')) and string[1:].isdigit()
) or string.isdigit()
TL;DR 答案:
我测试了 3 个主要变体 (1) try/except,(2) re.match() 和 (3) 字符串操作(见上文)。第三种变体比 try/except
和 re.match()
快大约两倍。顺便说一句:正则表达式变体是最慢的!请参阅下面的测试脚本。
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
9.66 | 7.03 | 4.86
,在 Python 3.8 上:7.78 | 5.56 | 4.57
我有一种根本不使用 int 的可能性,除非字符串不代表数字,否则不应引发异常
float(number)==float(number)//1
它应该适用于浮点接受的任何类型的字符串,正数,负数,工程符号......
我建议如下:
import ast
def is_int(s):
return isinstance(ast.literal_eval(s), int)
从 docs:
安全地评估包含 Python 文字或容器显示的表达式节点或字符串。提供的字符串或节点只能由以下 Python 文字结构组成:字符串、字节、数字、元组、列表、字典、集合、布尔值和无。
我应该注意,当针对不构成 Python 文字的任何内容调用时,这将引发 ValueError
异常。由于问题要求没有尝试/例外的解决方案,因此我有一个 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
¯\_(ツ)_/¯
'123abc'
- SyntaxError: unexpected EOF while parsing
这样的输入异常
检查后将值转换为字符串是整数,然后检查字符串的第一个字符值是 -
或 +
以及字符串的其余部分 isdigit
。最后只需检查 isdigit
。
test = ['1', '12015', '1..2', 'a2kk78', '1.5', 2, 1.24, '-8.5', '+88751.71', '-1', '+7']
查看
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
我想这个问题与速度有关,因为 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 解决方案要快得多。
呃..试试这个:
def int_check(a):
if int(a) == a:
return True
else:
return False
如果您不输入不是数字的字符串,则此方法有效。
而且(我忘了放数字检查部分。),有一个函数检查字符串是否是数字。它是 str.isdigit()。这是一个例子:
a = 2
a.isdigit()
如果调用 a.isdigit(),它将返回 True。
a
的值 2
周围加上引号。
int_check("ardvark")
不定期副业成功案例分享
u'²'.isdigit()
为真,但int(u'²')
引发 ValueError。请改用u.isdecimal()
。str.isdigit()
依赖于 Python 2 的语言环境。check_int('')
将引发异常而不是返回False