如何在Python中检查字符串是否表示数值?

def is_number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

上述方法可行,但似乎很笨拙。


如果您正在测试的内容来自用户输入,那么即使它表示int或float,它仍然是一个字符串。请参阅如何将输入读取为数字?用于转换输入,并询问用户输入,直到他们给出有效响应以确保输入在继续之前表示int或float(或其他要求)。


当前回答

我认为您的解决方案很好,但有一个正确的正则表达式实现。

对于这些答案,似乎有很多正则表达式的仇恨,我认为这是不合理的,正则表达式可以相当干净、正确和快速。这真的取决于你想做什么。最初的问题是如何“检查字符串是否可以表示为数字(浮点数)”(根据你的标题)。在检查了数值/浮点值是否有效后,您可能希望使用它,在这种情况下,try/except非常有意义。但是,如果出于某种原因,您只想验证字符串是数字,那么正则表达式也可以正常工作,但很难得到正确的结果。例如,我认为到目前为止,大多数正则表达式的答案都不能正确解析没有整数部分(如“.7”)的字符串,就python而言,整数部分是一个浮点数。在不需要小数部分的单个正则表达式中检查这一点有点困难。我包含了两个正则表达式来显示这一点。

它确实提出了一个有趣的问题,即“数字”是什么。您是否包含“inf”,它在python中作为浮点数有效?或者您是否包含“数字”但可能无法在python中表示的数字(例如大于float max的数字)。

解析数字的方式也存在歧义。例如,“--20”呢?这是一个“数字”吗?这是代表“20”的合法方式吗?Python将允许您执行“var=--20”并将其设置为20(尽管实际上这是因为它将其作为表达式处理),但float(“--20”)不起作用。

无论如何,在没有更多信息的情况下,这里有一个正则表达式,我相信它涵盖了python解析它们时的所有int和float。

# Doesn't properly handle floats missing the integer part, such as ".7"
SIMPLE_FLOAT_REGEXP = re.compile(r'^[-+]?[0-9]+\.?[0-9]+([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56"      # sign (-)
                            #     integer (12)
                            #           mantissa (34)
                            #                    exponent (E+56)

# Should handle all floats
FLOAT_REGEXP = re.compile(r'^[-+]?([0-9]+|[0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56"      # sign (-)
                            #     integer (12)
                            #           OR
                            #             int/mantissa (12.34)
                            #                            exponent (E+56)

def is_float(str):
  return True if FLOAT_REGEXP.match(str) else False

一些示例测试值:

True  <- +42
True  <- +42.42
False <- +42.42.22
True  <- +42.42e22
True  <- +42.42E-22
False <- +42.42e-22.8
True  <- .42
False <- 42nope

在@ron reiter的回答中运行基准测试代码表明,这个正则表达式实际上比普通正则表达式快,并且在处理错误值方面比异常快得多,这是有道理的。结果:

check_regexp with good floats: 18.001921
check_regexp with bad floats: 17.861423
check_regexp with strings: 17.558862
check_correct_regexp with good floats: 11.04428
check_correct_regexp with bad floats: 8.71211
check_correct_regexp with strings: 8.144161
check_replace with good floats: 6.020597
check_replace with bad floats: 5.343049
check_replace with strings: 5.091642
check_exception with good floats: 5.201605
check_exception with bad floats: 23.921864
check_exception with strings: 23.755481

其他回答

这个怎么样:

'3.14'.replace('.','',1).isdigit()

只有当有一个或没有“”时,它才会返回真在数字串中。

'3.14.5'.replace('.','',1).isdigit()

将返回false

编辑:刚刚看到另一条评论。。。可以为其他情况添加.replace(badstuff,“”,maxnum_badstuff)。如果你传递的是盐而不是任意的调味品(参考:xkcd#974),这会很好:P

对于非数字字符串,请尝试:except:实际上比正则表达式慢。对于有效数字的字符串,正则表达式速度较慢。因此,适当的方法取决于您的输入。

如果您发现自己处于性能绑定中,可以使用名为fastnumbers的新第三方模块,该模块提供一个名为isfloat的函数。完全披露,我是作者。我已将其结果包含在以下时间中。


from __future__ import print_function
import timeit

prep_base = '''\
x = 'invalid'
y = '5402'
z = '4.754e3'
'''

prep_try_method = '''\
def is_number_try(val):
    try:
        float(val)
        return True
    except ValueError:
        return False

'''

prep_re_method = '''\
import re
float_match = re.compile(r'[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$').match
def is_number_re(val):
    return bool(float_match(val))

'''

fn_method = '''\
from fastnumbers import isfloat

'''

print('Try with non-number strings', timeit.timeit('is_number_try(x)',
    prep_base + prep_try_method), 'seconds')
print('Try with integer strings', timeit.timeit('is_number_try(y)',
    prep_base + prep_try_method), 'seconds')
print('Try with float strings', timeit.timeit('is_number_try(z)',
    prep_base + prep_try_method), 'seconds')
print()
print('Regex with non-number strings', timeit.timeit('is_number_re(x)',
    prep_base + prep_re_method), 'seconds')
print('Regex with integer strings', timeit.timeit('is_number_re(y)',
    prep_base + prep_re_method), 'seconds')
print('Regex with float strings', timeit.timeit('is_number_re(z)',
    prep_base + prep_re_method), 'seconds')
print()
print('fastnumbers with non-number strings', timeit.timeit('isfloat(x)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print('fastnumbers with integer strings', timeit.timeit('isfloat(y)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print('fastnumbers with float strings', timeit.timeit('isfloat(z)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print()

Try with non-number strings 2.39108395576 seconds
Try with integer strings 0.375686168671 seconds
Try with float strings 0.369210958481 seconds

Regex with non-number strings 0.748660802841 seconds
Regex with integer strings 1.02021503448 seconds
Regex with float strings 1.08564686775 seconds

fastnumbers with non-number strings 0.174362897873 seconds
fastnumbers with integer strings 0.179651021957 seconds
fastnumbers with float strings 0.20222902298 seconds

如你所见

try:except:对于数字输入很快,但对于无效输入很慢当输入无效时,正则表达式非常有效fastnumbers在这两种情况下都获胜

在Alfe指出您不需要单独检查浮点数后更新,因为两者都是复杂的句柄:

def is_number(s):
    try:
        complex(s) # for int, long, float and complex
    except ValueError:
        return False

    return True

之前说过:在一些罕见的情况下,您可能还需要检查复数(例如1+2i),这不能用浮点数表示:

def is_number(s):
    try:
        float(s) # for int, long and float
    except ValueError:
        try:
            complex(s) # for complex
        except ValueError:
            return False

    return True

我想看看哪种方法最快。总的来说,check_replace函数给出了最佳和最一致的结果。check_exception函数给出了最快的结果,但前提是没有触发异常——这意味着它的代码是最有效的,但抛出异常的开销非常大。

请注意,检查成功的强制转换是唯一准确的方法,例如,这与check_exception一起工作,但其他两个测试函数将为有效的float返回False:

huge_number = float('1e+100')

以下是基准代码:

import time, re, random, string

ITERATIONS = 10000000

class Timer:    
    def __enter__(self):
        self.start = time.clock()
        return self
    def __exit__(self, *args):
        self.end = time.clock()
        self.interval = self.end - self.start

def check_regexp(x):
    return re.compile("^\d*\.?\d*$").match(x) is not None

def check_replace(x):
    return x.replace('.','',1).isdigit()

def check_exception(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

to_check = [check_regexp, check_replace, check_exception]

print('preparing data...')
good_numbers = [
    str(random.random() / random.random()) 
    for x in range(ITERATIONS)]

bad_numbers = ['.' + x for x in good_numbers]

strings = [
    ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(random.randint(1,10)))
    for x in range(ITERATIONS)]

print('running test...')
for func in to_check:
    with Timer() as t:
        for x in good_numbers:
            res = func(x)
    print('%s with good floats: %s' % (func.__name__, t.interval))
    with Timer() as t:
        for x in bad_numbers:
            res = func(x)
    print('%s with bad floats: %s' % (func.__name__, t.interval))
    with Timer() as t:
        for x in strings:
            res = func(x)
    print('%s with strings: %s' % (func.__name__, t.interval))

以下是2017年MacBook Pro 13上Python 2.7.10的结果:

check_regexp with good floats: 12.688639
check_regexp with bad floats: 11.624862
check_regexp with strings: 11.349414
check_replace with good floats: 4.419841
check_replace with bad floats: 4.294909
check_replace with strings: 4.086358
check_exception with good floats: 3.276668
check_exception with bad floats: 13.843092
check_exception with strings: 15.786169

以下是2017年MacBook Pro 13上Python 3.6.5的结果:

check_regexp with good floats: 13.472906000000009
check_regexp with bad floats: 12.977665000000016
check_regexp with strings: 12.417542999999995
check_replace with good floats: 6.011045999999993
check_replace with bad floats: 4.849356
check_replace with strings: 4.282754000000011
check_exception with good floats: 6.039081999999979
check_exception with bad floats: 9.322753000000006
check_exception with strings: 9.952595000000002

以下是2017年MacBook Pro 13上PyPy 2.7.13的结果:

check_regexp with good floats: 2.693217
check_regexp with bad floats: 2.744819
check_regexp with strings: 2.532414
check_replace with good floats: 0.604367
check_replace with bad floats: 0.538169
check_replace with strings: 0.598664
check_exception with good floats: 1.944103
check_exception with bad floats: 2.449182
check_exception with strings: 2.200056

强制转换为float并捕获ValueError可能是最快的方法,因为float()专门用于此。任何其他需要字符串解析(正则表达式等)的操作都可能会比较慢,因为它没有针对该操作进行调整。我的0.02美元。