Python:如何识别数字字符串?

1 投票
4 回答
1788 浏览
提问于 2025-04-15 19:10

我尝试了几种方法,其实我最关心的是性能,而不是正确性。我发现使用正则表达式的实现速度大约比使用类型转换的慢3到4倍。有没有其他更高效的方法呢?

def IsNumber(x):
    try:
        _ = float(x)
    except ValueError:
        return False
    return True

 def IsNumber2(x):
     import re
     if re.match("^\d*.?\d*$", x) == None:
         return False
     return True

谢谢!

4 个回答

2

这个问题的答案很大程度上取决于你对“数字字符串”的定义。如果你认为数字字符串是“任何浮点数可以接受的东西”,那么使用尝试-异常的方法可能是最好的选择。

不过要注意,浮点数的接受范围可能比你想象的要宽松:在大多数机器上,它会接受表示无穷大和非数字(nan)的字符串。例如,在我的机器上,它会接受 'nan(dead!$#parrot)'。它还会接受字符串前后的空格。而且根据你的应用需求,你可能想要排除浮点数的指数表示。在这种情况下,使用正则表达式会比较合适。如果只是想排除无穷大和非数字,使用尝试-异常的方法,然后用 math.isnan 和 math.isinf 来检查转换结果可能会更快。

写一个正确的用于数字字符串的正则表达式其实是个容易出错的任务。比如,你的 IsNumber2 函数会接受字符串 '.'。你可以在 decimal 模块的源代码中找到一个经过验证的数字字符串正则表达式。这里是它的样子(稍作修改):

_parser = re.compile(r"""        # A numeric string consists of:
    (?P<sign>[-+])?              # an optional sign, followed by either...
    (
        (?=\d|\.\d)              # ...a number (with at least one digit)
        (?P<int>\d*)             # having a (possibly empty) integer part
        (\.(?P<frac>\d*))?       # followed by an optional fractional part
        (E(?P<exp>[-+]?\d+))?    # followed by an optional exponent, or...
    |
        Inf(inity)?              # ...an infinity, or...
    |
        (?P<signal>s)?           # ...an (optionally signaling)
        NaN                      # NaN
        (?P<diag>\d*)            # with (possibly empty) diagnostic info.
    )
    \Z
""", re.VERBOSE | re.IGNORECASE | re.UNICODE).match

这个正则表达式几乎完全匹配浮点数可以接受的内容,除了前后的空格和一些关于非数字的细微差别(比如用于标识非数字的额外's'和诊断信息)。当我需要一个数字正则表达式时,我通常会从这个开始,然后删掉我不需要的部分。

注意:理论上,浮点数的处理速度可能会比正则表达式慢,因为它不仅要解析字符串,还要将其转换为浮点数,这个过程相对复杂;不过如果真是这样,那就很意外了。

2

其实不是。强制转换是处理这个问题的常用方法。

6

首先,它们并不是在做同样的事情。比如,浮点数可以写成“1e3”,而float()函数可以接受这种写法。这里说的不是“强制转换”,而是“转换”。

其次,不要在IsNumber2函数里导入re模块,特别是如果你想和timeit一起使用的话。应该把导入放在函数外面。

最后,float()函数更快这点我并不惊讶。因为它是用C语言专门写的一个程序,目的非常明确,而正则表达式需要先转换成一种可以被解释的形式。

你最初的版本,使用float()的那个,速度够快吗?应该是的,我不知道在Python里还有比这更好的方法来做同样的事情。

撰写回答