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

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

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


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


当前回答

这不仅丑陋而且缓慢

我对这两个都有异议。

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

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

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

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

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

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

其他回答

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

输入可能如下:

a=“50”b=50c=50.1d=“50.1”


1-一般输入:

这个函数的输入可以是一切!

查找给定变量是否为数字。数字字符串由可选符号、任意位数、可选小数部分和可选指数部分组成。因此,+0123.45e6是一个有效的数值。不允许使用十六进制(例如0xf4c3b000c)和二进制(例如0b10100111001)表示法。

is_numeric函数

import ast
import numbers              
def is_numeric(obj):
    if isinstance(obj, numbers.Number):
        return True
    elif isinstance(obj, str):
        nodes = list(ast.walk(ast.parse(obj)))[1:]
        if not isinstance(nodes[0], ast.Expr):
            return False
        if not isinstance(nodes[-1], ast.Num):
            return False
        nodes = nodes[1:-1]
        for i in range(len(nodes)):
            #if used + or - in digit :
            if i % 2 == 0:
                if not isinstance(nodes[i], ast.UnaryOp):
                    return False
            else:
                if not isinstance(nodes[i], (ast.USub, ast.UAdd)):
                    return False
        return True
    else:
        return False

测试:

>>> is_numeric("54")
True
>>> is_numeric("54.545")
True
>>> is_numeric("0x45")
True

is_float函数

查找给定变量是否为float。浮点数字符串由可选符号、任意数量的数字等组成。。。

import ast

def is_float(obj):
    if isinstance(obj, float):
        return True
    if isinstance(obj, int):
        return False
    elif isinstance(obj, str):
        nodes = list(ast.walk(ast.parse(obj)))[1:]
        if not isinstance(nodes[0], ast.Expr):
            return False
        if not isinstance(nodes[-1], ast.Num):
            return False
        if not isinstance(nodes[-1].n, float):
            return False
        nodes = nodes[1:-1]
        for i in range(len(nodes)):
            if i % 2 == 0:
                if not isinstance(nodes[i], ast.UnaryOp):
                    return False
            else:
                if not isinstance(nodes[i], (ast.USub, ast.UAdd)):
                    return False
        return True
    else:
        return False

测试:

>>> is_float("5.4")
True
>>> is_float("5")
False
>>> is_float(5)
False
>>> is_float("5")
False
>>> is_float("+5.4")
True

什么是ast?


2-如果您确信变量内容为字符串:

使用str.isdigit()方法

>>> a=454
>>> a.isdigit()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute 'isdigit'
>>> a="454"
>>> a.isdigit()
True

3-数字输入:

检测int值:

>>> isinstance("54", int)
False
>>> isinstance(54, int)
True
>>> 

检测浮子:

>>> isinstance("45.1", float)
False
>>> isinstance(45.1, float)
True

TL;DR最佳解决方案是s.replace('.','',1).isdigit()

我做了一些比较不同方法的基准测试

def is_number_tryexcept(s):
    """ Returns True if string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False
       
import re    
def is_number_regex(s):
    """ Returns True if string is a number. """
    if re.match("^\d+?\.\d+?$", s) is None:
        return s.isdigit()
    return True


def is_number_repl_isdigit(s):
    """ Returns True if string is a number. """
    return s.replace('.','',1).isdigit()

如果字符串不是数字,则except块非常慢。但更重要的是,try-except方法是正确处理科学符号的唯一方法。

funcs = [
          is_number_tryexcept, 
          is_number_regex,
          is_number_repl_isdigit
          ]

a_float = '.1234'

print('Float notation ".1234" is not supported by:')
for f in funcs:
    if not f(a_float):
        print('\t -', f.__name__)

以下项不支持浮点符号“.1234”:

is_number_regex编号科学1='1.000000e+50'科学2=“1e50”print('不支持科学符号“1.0000000e+50”:')对于函数中的f:如果不是f(科学1):打印('\t-',f.name)print('不支持科学符号“1e50”:')对于函数中的f:如果不是f(科学2):打印('\t-',f.name)

以下各项不支持科学符号“1.0000000e+50”:

is_number_regex编号is_number_repl_isdigit编号以下各项不支持科学符号“1e50”:is_number_regex编号is_number_repl_isdigit编号

编辑:基准结果

import timeit

test_cases = ['1.12345', '1.12.345', 'abc12345', '12345']
times_n = {f.__name__:[] for f in funcs}

for t in test_cases:
    for f in funcs:
        f = f.__name__
        times_n[f].append(min(timeit.Timer('%s(t)' %f, 
                      'from __main__ import %s, t' %f)
                              .repeat(repeat=3, number=1000000)))

测试了以下功能

from re import match as re_match
from re import compile as re_compile

def is_number_tryexcept(s):
    """ Returns True if string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False

def is_number_regex(s):
    """ Returns True if string is a number. """
    if re_match("^\d+?\.\d+?$", s) is None:
        return s.isdigit()
    return True


comp = re_compile("^\d+?\.\d+?$")    

def compiled_regex(s):
    """ Returns True if string is a number. """
    if comp.match(s) is None:
        return s.isdigit()
    return True


def is_number_repl_isdigit(s):
    """ Returns True if string is a number. """
    return s.replace('.','',1).isdigit()

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

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

这是我的简单方法。假设我在循环一些字符串,如果它们变成数字,我想将它们添加到数组中。

try:
    myvar.append( float(string_to_check) )
except:
    continue

如果myvar.apppend是一个数字,则将其替换为要对字符串执行的任何操作。其想法是尝试使用float()操作,并使用返回的错误来确定字符串是否为数字。