How can I check if a string represents an int, without using try/except?
有没有办法判断字符串是否表示整数(例如,
1 2 | is_int('3.14') = False is_int('-7') = True |
使用正整数,您可以使用
1 2 | >>> '16'.isdigit() True |
但它不适用于负整数。假设你可以尝试以下方法:
1 2 3 | >>> s = '-17' >>> s.startswith('-') and s[1:].isdigit() True |
它不适用于
编辑:
1 2 3 4 | def check_int(s): if s[0] in ('-', '+'): return s[1:].isdigit() return s.isdigit() |
如果你真的只是在整个地方使用
1 2 3 4 5 6 7 8 9 10 11 | def RepresentsInt(s): try: int(s) return True except ValueError: return False >>> print RepresentsInt("+123") True >>> print RepresentsInt("10.0") False |
这将是更多的代码来完全覆盖Python认为整数的所有字符串。我说这只是pythonic。
你知道,我已经找到了(而且我已经反复测试过这种情况),无论出于何种原因,尝试/除了不能很好地执行。我经常尝试几种做事方式,我不认为我曾经找到过使用try / except来执行最佳测试的方法,事实上在我看来这些方法通常都接近于最坏的,如果不是最坏的。并非在所有情况下,但在许多情况下。我知道很多人都说这是"Pythonic"的方式,但这是我与他们分道扬with的一个方面。对我而言,它既不是非常高效也不是非常优雅,因此,我倾向于仅将其用于错误捕获和报告。
我会抱怨PHP,perl,ruby,C,甚至是怪异的shell都有简单的函数来测试整数引擎的字符串,但是在验证这些假设的尽职调查让我感到沮丧!显然这种缺乏是一种常见的疾病。
这是Bruno的帖子快速而又脏的编辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | 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(" ") 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)) |
以下是性能比较结果:
1 2 3 4 5 6 | 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()函数以静默方式截断浮点数的小数部分,并返回小数之前的整数部分,除非浮点数首先转换为字符串。
对于像0.0和1.0这样的值(技术上是整数),check_int()函数返回false,对于像'06'这样的值,返回true。
以下是当前(Python 3.5)测试结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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 | |
刚才我尝试添加这个功能:
1 2 3 4 5 | 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,所以。我想,选择你的毒药。
使用正则表达式:
1 2 3 | import re def RepresentsInt(s): return re.match(r"[-+]?\d+$", s) is not None |
如果您还必须接受小数分数:
1 2 | def RepresentsInt(s): return re.match(r"[-+]?\d+(\.0*)?$", s) is not None |
如果您经常这样做,为了提高性能,请使用
例子:
str.isdigit("abc") ## False
str.isdigit("23.4") ## False
编辑:
正如@BuzzMoschetti指出的那样,这种方式对于减号(例如"-23")将失败。如果您的input_num可能小于0,请在应用str.isdigit()之前使用re.sub(regex_search,regex_replace,contents)。例如:
input_num="-23"
input_num = re.sub("^-","", input_num) #"^" indicates to remove the first"-" only.
str.isdigit(input_num) ## True
适当的RegEx解决方案将结合Greg Hewgill和Nowell的想法,但不使用全局变量。您可以通过将属性附加到方法来完成此操作。另外,我知道将导入放入方法中是不赞成的,但我想要的是像http://peak.telecommunity.com/DevCenter/Importing#lazy-imports这样的"懒惰模块"效果。
编辑:到目前为止,我最喜欢的技术是使用String对象的专有方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #!/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)) |
对于不太冒险的班级成员,这是输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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 |
1 2 3 4 5 6 7 8 | >>>"+7".lstrip("-+").isdigit() True >>>"-7".lstrip("-+").isdigit() True >>>"7".lstrip("-+").isdigit() True >>>"13.4".lstrip("-+").isdigit() False |
所以你的功能是:
1 2 | def is_int(val): return val[1].isdigit() and val.lstrip("-+").isdigit() |
Greg Hewgill的方法缺少一些组件:前导"^"只匹配字符串的开头,并预先编译re。但是这种方法可以让你避免尝试:exept:
1 2 3 4 | import re INT_RE = re.compile(r"^[-]?\d+$") def RepresentsInt(s): return INT_RE.match(str(s)) is not None |
我会感兴趣为什么你试图避免尝试:除了?
我认为
1 | s.startswith('-') and s[1:].isdigit() |
最好重写为:
1 | s.replace('-', '').isdigit() |
因为s [1:]也会创建一个新字符串
但更好的解决方案是
1 | s.lstrip('+-').isdigit() |
我真的很喜欢Shavais的帖子,但我又增加了一个测试用例(&amp;内置的isdigit()函数):
1 2 3 4 5 6 7 8 9 10 11 12 | 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() |
它显着地一致地击败了其余的时间:
1 2 3 4 5 6 7 | 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:
1 2 | $ python --version Python 2.7.10 |
我添加的两个测试用例(isInt_loop和isInt_digit)都传递完全相同的测试用例(它们都只接受无符号整数),但我认为人们可能更聪明地修改字符串实现(isInt_loop)而不是内置的isdigit ()函数,所以我包括它,即使执行时间略有不同。 (这两种方法都打败了其他所有方法,但是没有处理额外的东西:"./+ / -")
此外,我确实发现有趣的是,正则表达式(isInt_re2方法)在Shavais在2012年(目前为2018年)执行的相同测试中击败了字符串比较。也许正则表达式库已得到改进?
这是一个解析而不会引发错误的函数。它处理明显的情况在失败时返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #!/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) |
一些测试:
1 2 3 4 | 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))) |
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 |
根据您的需求,您可以使用:
1 2 | def int_predicate(number): return get_int(number) is not None |
在我看来,这可能是接近它的最简单和pythonic方式。我没有看到这个解决方案,它与正则表达式基本相同,但没有正则表达式。
1 2 3 | def is_int(test): import string return not (set(test) - set(string.digits)) |
我想这个问题与速度有关,因为try / except有时间损失:
&nbsp;测试数据
首先,我创建了一个包含200个字符串,100个失败字符串和100个数字字符串的列表。
1 2 3 4 5 6 | from random import shuffle numbers = [u'+1'] * 100 nonumbers = [u'1abc'] * 100 testlist = numbers + nonumbers shuffle(testlist) testlist = np.array(testlist) |
&nbsp; numpy解决方案(仅适用于数组和unicode)
np.core.defchararray.isnumeric也可以使用unicode字符串
1 2 3 | import numpy as np %timeit np.core.defchararray.isnumeric(testlist) 10000 loops, best of 3: 27.9 μs per loop # 200 numbers per loop |
尝试/除外
1 2 3 4 5 6 7 8 9 10 11 12 | 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解决方案要快得多。
我有一种可能根本不使用int,除非字符串不表示数字,否则不应引发异常
1 | float(number)==float(number)//1 |
它适用于浮动接受的任何类型的字符串,正面,负面,工程符号......
呃..试试这个:
1 2 3 4 5 | def int_check(a): if int(a) == a: return True else: return False |
如果您没有放置不是数字的字符串,则此方法有效。
而且(我忘了把数字检查部分。),有一个函数检查字符串是否是一个数字。它是str.isdigit()。这是一个例子:
1 2 | a = 2 a.isdigit() |
如果你调用a.isdigit(),它将返回True。