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

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

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


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


当前回答

我想看看哪种方法最快。总的来说,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

其他回答

这不仅丑陋而且缓慢

我对这两个都有异议。

正则表达式或其他字符串解析方法会更丑陋、更慢。

我不确定有什么比上面提到的更快。它调用函数并返回。Try/Catch不会带来太多的开销,因为最常见的异常是在不大量搜索堆栈帧的情况下捕获的。

问题是任何数值转换函数都有两种结果

一个数字,如果该数字有效状态代码(例如,通过errno)或异常,表明无法解析任何有效数字。

C(作为一个例子)通过多种方式解决了这个问题。Python将其清晰明确地展示出来。

我认为你这样做的代码是完美的。

通过返回比True和False更有用的值,可以以有用的方式概括异常技术。例如,此函数将引号放在字符串周围,但不使用数字。这正是我需要的快速而肮脏的过滤器来为R定义一些变量。

import sys

def fix_quotes(s):
    try:
        float(s)
        return s
    except ValueError:
        return '"{0}"'.format(s)

for line in sys.stdin:
    input = line.split()
    print input[0], '<- c(', ','.join(fix_quotes(c) for c in input[1:]), ')'

您可能需要考虑一个例外:字符串“NaN”

如果您希望is_number为“NaN”返回FALSE,则此代码将无法工作,因为Python将其转换为非数字的数字表示(请讨论身份问题):

>>> float('NaN')
nan

否则,我应该感谢您现在广泛使用的这段代码

G.

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

对于这些答案,似乎有很多正则表达式的仇恨,我认为这是不合理的,正则表达式可以相当干净、正确和快速。这真的取决于你想做什么。最初的问题是如何“检查字符串是否可以表示为数字(浮点数)”(根据你的标题)。在检查了数值/浮点值是否有效后,您可能希望使用它,在这种情况下,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

只有Mimic C#

在C#中,有两个不同的函数处理标量值的解析:

Float.Parse()Float.TryParse()

float.parse():

def parse(string):
    try:
        return float(string)
    except Exception:
        throw TypeError

注意:如果您想知道为什么我将异常更改为TypeError,请参阅以下文档。

float.try_parse():

def try_parse(string, fail=None):
    try:
        return float(string)
    except Exception:
        return fail;

注意:您不希望返回布尔值“False”,因为这仍然是一个值类型。没有更好的,因为它表明失败。当然,如果您想要一些不同的东西,可以将fail参数更改为您想要的任何值。

要扩展float以包含“parse()”和“try_parse()”,您需要对“float”类进行monkeypatch以添加这些方法。

如果你想尊重已有的函数,代码应该是这样的:

def monkey_patch():
    if(!hasattr(float, 'parse')):
        float.parse = parse
    if(!hasattr(float, 'try_parse')):
        float.try_parse = try_parse

侧注:我个人更喜欢称之为“猴子打拳”,因为我这样做时感觉就像在滥用语言,但YMMV除外。

用法:

float.parse('giggity') // throws TypeException
float.parse('54.3') // returns the scalar value 54.3
float.tryParse('twank') // returns None
float.tryParse('32.2') // returns the scalar value 32.2

伟大的蟒蛇圣人对罗马教廷夏皮索斯说:“你能做的任何事,我都能做得更好;我能做得比你更好。”