ChatGPT解决这个技术问题 Extra ChatGPT

如何检查字符串是否为数字(浮点数)?

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

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

上述工作,但它似乎笨重。

您当前的解决方案有什么问题?它简短、快速且易读。
而且您不必只返回 True 或 False。您可以返回适当修改的值 - 例如,您可以使用它来将非数字放在引号中。
在成功转换的情况下返回 float(s) 的结果不是更好吗?您仍然可以检查是否成功(结果为 False),并且您实际上已经进行了转换,无论如何您都可能想要它。
尽管这个问题比较老,但我只想说这是一种优雅的方式,记录为 EAFP。所以可能是此类问题的最佳解决方案。
不要在失败时返回 float(s) 或 None 的结果。如果您随后将其用作 x = float('0.00'); if x: use_float(x);,那么您的代码中就会出现错误。真实值是这些函数引发异常而不是首先返回 None 的原因。更好的解决方案是避免使用实用程序函数,并在您想使用它时将调用包围在 try catch 中。

M
Mateen Ulhaq

仅对于非负(无符号)整数,使用 isdigit()

>>> a = "03523"
>>> a.isdigit()
True
>>> b = "963spam"
>>> b.isdigit()
False

isdigit() 的文档:Python2Python3

对于 Python 2 Unicode 字符串:isnumeric()


这也是负面的负面影响
指数也失败:'1e3'.isdigit() --> False
虽然 Number != Digit,但正在寻找方法来测试字符串是否包含整数的人很可能会偶然发现这个问题,而 isDigit 方法很可能非常适合他们的应用。
@AdamParkin:isdigit()int() 对什么是整数有不同的看法,例如,对于 Unicode 字符 u'\u00b9'u'¹'.isdigit()Trueint(u'¹') 会引发 ValueError。
+1: isdigit() 可能不是 OP 想要的,但这正是我想要的。这个答案和方法可能并非涵盖所有类型的数字,但它仍然具有高度相关性,这与关于其准确性的争论相反。虽然“数字!= 数字”,数字仍然是数字的子集,尤其是正数、非负数并使用以 1-10 为底的数字。此外,对于要检查字符串是否为数字 ID 的情况,此方法特别有用且简短,这通常属于我刚刚描述的数字子集。
A
Alec

这不仅丑陋而且缓慢

我对两者都有争议。

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

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

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

一个数字,如果该数字有效

一个状态码(例如,通过 errno)或异常表明没有有效的数字可以被解析。

C(例如)以多种方式解决了这个问题。 Python 清楚而明确地列出了它。

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


我不认为代码是完美的(但我认为它非常接近):更常见的做法是将 only 放在 try 子句中“测试”的部分,所以我会放tryelse 子句中的 return True。原因之一是,对于问题中的代码,如果我必须查看它,我必须检查 try 子句中的第二条语句不能引发 ValueError:granted,这不需要太多时间或脑力,但为什么在不需要的时候使用脑力?
答案似乎很有说服力,但让我想知道为什么它不是开箱即用的......我会复制它并在任何情况下使用它。
真可怕。如果我不在乎数字 是什么 只是它是一个数字(这就是我来到这里的原因),那怎么样?而不是 1 行 IsNumeric() 我要么以 try/catch 结尾,要么以另一个包装 try/catch 结尾。啊
它不是“开箱即用”提供的,因为 if is_number(s): x = float(x) else: // fail 的代码行数与 try: x = float(x) catch TypeError: # fail 相同。这个效用函数是一个完全不必要的抽象。
但是抽象是库的全部意义所在。拥有一个“isNumber”函数(在任何语言中)都有很大帮助,因为您可以将它直接构建到 if 语句中,并且依赖于 try-catch 块的代码更具可读性和可维护性。此外,如果您需要在多个类/模块中多次使用代码,那么您使用的代码行数将超过内置函数。
I
Idok

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

我做了一些benchmarks比较不同方法

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

import re    
def is_number_regex(s):
    """ Returns True is 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 is string is a number. """
    return s.replace('.','',1).isdigit()

如果字符串不是数字,则 except-block 非常慢。但更重要的是,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

scientific1 = '1.000000e+50'
scientific2 = '1e50'


print('Scientific notation "1.000000e+50" is not supported by:')
for f in funcs:
    if not f(scientific1):
        print('\t -', f.__name__)




print('Scientific notation "1e50" is not supported by:')
for f in funcs:
    if not f(scientific2):
        print('\t -', f.__name__)

科学记数法“1.000000e+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 is string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False

def is_number_regex(s):
    """ Returns True is 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 is string is a number. """
    if comp.match(s) is None:
        return s.isdigit()
    return True


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

https://i.stack.imgur.com/DFoK6.png


对于漂亮的图表+1。我看到了基准和图表,所有的 TL;DR 事情变得清晰直观。
此方法不处理负数(破折号)。我提倡只使用 float 方法,因为它不太容易出错并且每次都可以使用。
需要注意的重要一点是,即使假设不能有破折号,replace-isdigit 方法只对非数字更快(错误结果),而 try-except 方法对数字更快(真实结果)。如果您的大部分输入都是有效输入,那么您最好使用 try-except 解决方案!
不适用于像 '1.5e-9' 这样的指数符号或负数。
太棒了, 除了明显的求幂和负数的假负警告外,您只需链接 s.replace() 调用即可轻松纠正。例如,s.replace('.','',1).replace('e-','',1).replace('e','',1).isdigit() 处理求幂。然后处理负数,如果是破折号,只需将第一个字符左剥离。例如,s.lstrip('-').replace('.','',1).replace('e-','',1).replace('e','',1).isdigit()是的,我已经彻底测试了该单行代码,并且可以确认它的行为符合预期。
W
W7GVR

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

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

>>> float('NaN')
nan

否则,我实际上应该感谢您提供我现在广泛使用的代码。 :)

G。


实际上,如果传递的文本实际上不是数字的表示,则 NaN 可能是一个很好的返回值(而不是 False)。检查它是一种痛苦(Python 的 float 类型确实需要一种方法),但是您可以在计算中使用它而不会产生错误,并且只需要检查结果。
另一个例外是字符串 'inf'infNaN 也可以以 +- 为前缀,并且仍然被接受。
如果要为 NaN 和 Inf 返回 False,请将行更改为 x = float(s);返回 (x == x) 和 (x - 1 != x)。这应该为除 Inf 和 NaN 之外的所有浮点数返回 True
x-1 == x 适用于小于 inf 的大浮点数。从 Python 3.2 开始,您可以使用 math.isfinite 来测试既不是 NaN 也不是无限的数字,或者在此之前同时检查 math.isnanmath.isinf
D
David C

这个怎么样:

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

仅当有一个或没有“。”时才会返回 true。在数字串中。

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

将返回 false

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


然而,这并没有考虑负数。
或者带有指数的数字,例如 1.234e56(也可以写成 +1.234E+56 和其他几个变体)。
re.match(r'^[+-]*(0[xbo])?[0-9A-Fa-f]*\.?[0-9A-Fa-f]*(E[+-]*[0-9A-Fa-f]+)$', 'str') 应该更好地确定一个数字(但不是全部,我不是这么说的)。我不建议使用这个,最好使用提问者的原始代码。
如果您不喜欢此解决方案,请在投票前阅读 this
伙计,这是我在这个网站上见过的最聪明的解决方案!干得好伙计!
3
3 revs

在 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

我不同意。这在正常使用中是非常不可能的,您最好在使用它们时构建一个 is_complex_number() 调用,而不是为 0.0001% 的误操作几率增加额外的操作来负担调用。
您可以完全剥离 float() 的内容,只需检查 complex() 调用是否成功。 float() 解析的所有内容都可以由 complex() 解析。
此函数将 Pandas 的 NaN 和 Inf 值作为数值返回。
complex('(01989)') 将返回 (1989+0j)。但是 float('(01989)') 会失败。所以我认为使用 complex 不是一个好主意。
Yikes. 奇怪的是,complex() 接受以 () 分隔的语法 - 大概是为了解释复数下的假想平面中的复合向量加法,但仍然如此。正如@plhn 建议的那样,在此处使用 complex() 会导致误报。 不要在生产代码中这样做。老实说,s.lstrip('-').replace('.','',1).replace('e-','',1).replace('e','',1).isdigit() 仍然是大多数用例的最佳解决方案。
J
Jason Baker

这不仅丑陋而且缓慢,而且看起来很笨重。

可能需要一些时间来适应,但这是 Python 的做法。正如已经指出的那样,替代方案更糟。但是以这种方式做事还有另一个优点:多态性。

鸭子打字背后的核心思想是“如果它像鸭子一样走路和说话,那么它就是鸭子”。如果您决定需要对字符串进行子类化,以便更改确定某些内容是否可以转换为浮点数的方式,该怎么办?或者,如果您决定完全测试其他对象怎么办?你可以做这些事情而不必改变上面的代码。

其他语言通过使用接口来解决这些问题。我将保存对另一个线程更好的解决方案的分析。不过,关键是 python 绝对是等式的鸭子类型,如果你打算在 Python 中进行大量编程,你可能不得不习惯这样的语法(但这并不意味着你当然必须喜欢它)。

您可能需要考虑的另一件事:与许多其他语言相比,Python 在抛出和捕获异常方面非常快(例如,比 .Net 快 30 倍)。哎呀,语言本身甚至会抛出异常来传达非异常的正常程序条件(每次使用 for 循环时)。因此,在您注意到一个重大问题之前,我不会太担心这段代码的性能方面。


Python 对基本函数使用异常的另一个常见位置是在 hasattr() 中,它只是包装在 try/except 中的 getattr() 调用。尽管如此,异常处理还是比正常的流控制要慢,所以将它用于大部分时间会发生的事情可能会导致性能下降。
看来,如果你想要一个单线,你就是 SOL
同样 pythonic 的想法是“请求宽恕比请求许可更好”,关于拥有廉价例外的影响。
S
Sdwdaw

对于 int,请使用:

>>> "1221323".isdigit()
True

但是对于 float,我们需要一些技巧 ;-)。每个浮点数都有一个点...

>>> "12.34".isdigit()
False
>>> "12.34".replace('.','',1).isdigit()
True
>>> "12.3.4".replace('.','',1).isdigit()
False

同样对于负数只需添加 lstrip()

>>> '-12'.lstrip('-')
'12'

现在我们得到了一个通用的方法:

>>> '-12.34'.lstrip('-').replace('.','',1).isdigit()
True
>>> '.-234'.lstrip('-').replace('.','',1).isdigit()
False

不处理像 1.234e56 和类似的事情。另外,我很想知道您如何发现 99999999999999999999e99999999999999999999 不是数字。试图解析它很快就会发现。
这比在 50m 字符串列表上的公认解决方案快约 30%,在 5k 字符串列表上快 150%。 👏
请注意,>>> '--1234'.lstrip('-').replace('.','',1).isdigit() 返回 true;也许不是预期的
M
Moinuddin Quadri

该答案提供了具有示例功能的分步指南,以查找字符串是:

正整数

正/负 - 整数/浮点数

检查数字时如何丢弃“NaN”(不是数字)字符串?

检查字符串是否为正整数

您可以使用 str.isdigit() 检查给定的字符串是否为 整数。

样本结果:

# For digit
>>> '1'.isdigit()
True
>>> '1'.isalpha()
False

检查字符串为正/负 - 整数/浮点

如果字符串是 数字或浮点数,str.isdigit() 将返回 False。例如:

# returns `False` for float
>>> '123.3'.isdigit()
False
# returns `False` for negative number
>>> '-123'.isdigit()
False

如果您想同时检查 整数和 float,那么您可以编写一个自定义函数来检查它:

def is_number(n):
    try:
        float(n)   # Type-casting the string to `float`.
                   # If string is not a valid `float`, 
                   # it'll raise `ValueError` exception
    except ValueError:
        return False
    return True

样品运行:

>>> is_number('123')    # positive integer number
True

>>> is_number('123.4')  # positive float number
True
 
>>> is_number('-123')   # negative integer number
True

>>> is_number('-123.4') # negative `float` number
True

>>> is_number('abc')    # `False` for "some random" string
False

在检查数字时丢弃“NaN”(不是数字)字符串

上述函数将为“NAN”(非数字)字符串返回 True,因为对于 Python,它是表示它不是数字的有效浮点数。例如:

>>> is_number('NaN')
True

为了检查数字是否为“NaN”,您可以使用 math.isnan() 作为:

>>> import math
>>> nan_num = float('nan')

>>> math.isnan(nan_num)
True

或者,如果您不想导入其他库来检查这一点,那么您可以简单地通过使用 == 将其与自身进行比较来检查它。当 nan float 与自身进行比较时,Python 返回 False。例如:

# `nan_num` variable is taken from above example
>>> nan_num == nan_num
False

因此,上面的 函数 is_number 可以更新为为 "NaN" 返回 False

def is_number(n):
    is_number = True
    try:
        num = float(n)
        # check for "nan" floats
        is_number = num == num   # or use `math.isnan(num)`
    except ValueError:
        is_number = False
    return is_number

样品运行:

>>> is_number('Nan')   # not a number "Nan" string
False

>>> is_number('nan')   # not a number string "nan" with all lower cased
False

>>> is_number('123')   # positive integer
True

>>> is_number('-123')  # negative integer
True

>>> is_number('-1.12') # negative `float`
True

>>> is_number('abc')   # "some random" string
False

PS:每次检查的每个操作都取决于号码的类型,会带来额外的开销。选择符合您要求的 is_number 函数版本。


S
SethMMorton

对于非数字字符串,try: 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

如你看到的

尝试:除了:对于数字输入来说速度很快,但对于无效输入来说非常慢

当输入无效时,正则表达式非常有效

fastnumbers 在这两种情况下都获胜


我站得更正了:-}它看起来不像是这样做的。也许使用像 prep_code_basisprep_code_re_method 这样的名称可以避免我的错误。
您介意解释一下您的模块是如何工作的,至少对于 isfloat 函数是这样吗?
@SolomonUcko 这是字符串检查部分源代码的链接:github.com/SethMMorton/fastnumbers/blob/v1.0.0/src/…。基本上,它按顺序遍历字符串中的每个字符,并验证它是否遵循有效浮点数的模式。如果输入已经是一个数字,它只使用快速 PyFloat_Check
针对此线程中的最佳替代方案进行了测试,我确认此解决方案到目前为止是最快的。第二快的方法是 str(s).strip('-').replace('.','',1).isdigit(),它大约慢了 10 倍!
请注意,timeit.timeit 将语句运行 100 万次。我很困惑为什么这些数字看起来这么慢。
E
Evan Plaice

只是模仿 C#

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

Float.Parse()

Float.TryParse()

浮动.解析():

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

注意:如果您想知道为什么我将异常更改为 TypeError,here's the documentation

float.try_parse():

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

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

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

如果你想尊重预先存在的功能,代码应该是这样的:

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

旁注:我个人更喜欢称它为 Monkey Punching,因为当我这样做时感觉就像我在滥用语言,但 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

伟大的圣人 Pythonas 对教廷 Sharpisus 说:“你能做的任何事我都能做得更好;我能做的任何事都比你更好。”


我最近主要使用 JS 进行编码,但实际上并没有对此进行测试,因此可能会出现一些小错误。如果您看到任何内容,请随时纠正我的错误。
要添加对复数的支持,请参阅@Matthew Wilcoxson 的答案。 stackoverflow.com/a/3335060/290340
使用 ! 而不是 not 可能是一个小错误,但您绝对不能将属性分配给 CPython 中的内置 float
因不加选择地捕获所有异常以及使用不存在“抛出”的关键字而被否决
我是标记“不回答问题”的人。
A
Aruthawolf

我知道这是特别古老的,但我会添加一个答案,我相信它涵盖了最高投票答案中缺少的信息,这对任何发现此问题的人来说都非常有价值:

对于以下每种方法,如果您需要接受任何输入,请将它们与计数连接起来。 (假设我们使用整数的声音定义而不是 0-255 等)

x.isdigit() 适用于检查 x 是否为整数。

x.replace('-','').isdigit() 适用于检查 x 是否为负数。(检查 - 在第一个位置)

x.replace('.','').isdigit() 适用于检查 x 是否为小数。

x.replace(':','').isdigit() 适用于检查 x 是否为比率。

x.replace('/','',1).isdigit() 适用于检查 x 是否为分数。


虽然对于分数,您可能需要执行 x.replace('/','',1).isdigit() 否则日期(例如 2017 年 4 月 7 日)会被误解为数字。
对于链接条件的最佳方式:stackoverflow.com/q/3411771/5922329
c
codelogic

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


您的“2e-2”美元也是浮动的(使用浮动的另一个参数:)
@tzot 永远不要使用浮点数来表示货币价值。
@Luke:我完全同意你的看法,尽管我从未建议使用浮点数来表示货币价值;我刚刚说过货币价值可以表示为浮点数:)
B
Blackzafiro

您可以使用 Unicode 字符串,它们有一种方法可以满足您的需求:

>>> s = u"345"
>>> s.isnumeric()
True

或者:

>>> s = "345"
>>> u = unicode(s)
>>> u.isnumeric()
True

http://www.tutorialspoint.com/python/string_isnumeric.htm

http://docs.python.org/2/howto/unicode.html


对于非负整数是可以的;-)
s.isdecimal() 检查 s 字符串是否为非负整数。 s.isnumeric() 包括 int() 拒绝的字符。
a
a1an

因此,综上所述,检查 Nan、无穷大和复数(似乎它们是用 j 指定的,而不是 i,即 1+2j),结果是:

def is_number(s):
    try:
        n=str(float(s))
        if n == "nan" or n=="inf" or n=="-inf" : return False
    except ValueError:
        try:
            complex(s) # for complex
        except ValueError:
            return False
    return True

到目前为止最好的答案。谢谢
R
Ron Reiter

我想看看哪种方法最快。总体而言,check_replace 函数给出了最好和最一致的结果。 check_exception 函数给出了最快的结果,但前提是没有引发异常 - 这意味着它的代码是最有效的,但引发异常的开销非常大。

请注意,检查是否成功转换是唯一准确的方法,例如,这适用于 check_exception,但其他两个测试函数将返回 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

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

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

以下是 PyPy 2.7.13 在 2017 MacBook Pro 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

您还应该测试无效案例的性能。这些数字也不例外,这正是“慢”部分。
@UgoMéda 我从 2013 年开始接受你的建议并做到了:)
“请注意,检查是否成功转换是唯一准确的方法” <- 这实际上不是真的。我在上面的答案中使用正则表达式运行了您的测试,它实际上比正则表达式运行得更快。我会将结果添加到上面的答案中。
顺便说一句,有趣的是,您的坏号码创建者实际上可以创建一些合法号码,尽管这种情况非常罕见。 :)
B
Bastian

输入可能如下:

a="50" b=50 c=50.1 d="50.1"

1-一般输入:

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

查找给定变量是否为数字。数字字符串由可选的符号、任意位数、可选的小数部分和可选的指数部分组成。因此 +0123.45e6 是一个有效的数值。不允许使用十六进制(例如 0xf4c3b00c)和二进制(例如 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 函数

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

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

什么是“ast”?
类型上的 insight
这在 is_numeric("String 1") Wrapped the method in try/except 的测试下失败并且可以工作。
G
Georgy

str.isnumeric()

如果字符串中的所有字符都是数字字符,并且至少有一个字符,则返回 True,否则返回 False。数字字符包括数字字符,以及所有具有 Unicode 数值属性的字符,例如 U+2155、VULGAR FRACTION ONE FIFTH。形式上,数字字符是具有属性值 Numeric_Type=Digit、Numeric_Type=Decimal 或 Numeric_Type=Numeric 的字符。

str.isdecimal()

如果字符串中的所有字符都是十进制字符并且至少有一个字符,则返回 True,否则返回 False。十进制字符是可用于形成以 10 为底的数字的字符,例如 U+0660、ARABIC-INDIC DIGIT ZERO。形式上,十进制字符是 Unicode 通用类别“Nd”中的字符。

两者都可用于 Python 3.0 中的字符串类型。


S
Siddharth Satpathy

在浮点数的最一般情况下,人们希望处理整数和小数。我们以字符串 "1.1" 为例。

我会尝试以下方法之一:

1.> isnumeric()

word = "1.1"

"".join(word.split(".")).isnumeric()
>>> True

2.> isdigit()

word = "1.1"

"".join(word.split(".")).isdigit()
>>> True

3.> isdecimal()

word = "1.1"

"".join(word.split(".")).isdecimal()
>>> True

速度:

► 所有上述方法都具有相似的速度。

%timeit "".join(word.split(".")).isnumeric()
>>> 257 ns ± 12 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit "".join(word.split(".")).isdigit()
>>> 252 ns ± 11 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit "".join(word.split(".")).isdecimal()
>>> 244 ns ± 7.17 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

对于所有这些示例,您正在检查 11 是否是数字,而不是 1.1
a
astrodsg

我需要确定一个字符串是否转换为基本类型(float、int、str、bool)。在互联网上找不到任何东西后,我创建了这个:

def str_to_type (s):
    """ Get possible cast type for a string

    Parameters
    ----------
    s : string

    Returns
    -------
    float,int,str,bool : type
        Depending on what it can be cast to

    """    
    try:                
        f = float(s)        
        if "." not in s:
            return int
        return float
    except ValueError:
        value = s.upper()
        if value == "TRUE" or value == "FALSE":
            return bool
        return type(s)

例子

str_to_type("true") # bool
str_to_type("6.0") # float
str_to_type("6") # int
str_to_type("6abc") # str
str_to_type(u"6abc") # unicode       

您可以捕获类型并使用它

s = "6.0"
type_ = str_to_type(s) # float
f = type_(s) 

很好的答案,但不完全通用:str_to_type("123e-4") 返回 int,而 float("123e-4") 返回 0.0123。此外,str_to_type("NaN") 返回 int
if "." not in s: 更改为 if "." not in s and "e" not in s and "N" not in s: 修复了我在此处显示的情况,但它使代码更加笨拙,我不确定它是否会捕获所有情况。
我的首选解决方案也是更安全的解决方案是使用嵌套的 try/except 语句:将 i = int(s); return int 包装到第一个 try/except 中,然后将 f = float(s); return float 放入第二个 try/except 中,第一个 try 的所有 except 块/除了。像 user1508746's answer 的结构,具有不同的返回值。
D
David Ljung Madison Stellar

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

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

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

解析数字的方式也有歧义。例如,“--20”呢?这是一个“数字”吗?这是代表“20”的合法方式吗? Python 会让你执行 "var = --20" 并将其设置为 20(虽然这实际上是因为它把它视为一个表达式),但 float("--20") 不起作用。

无论如何,没有更多信息,这是一个正则表达式,我相信它涵盖了所有整数和浮点数,因为 python 解析它们。

# 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 的answer 中运行基准测试代码表明,这个正则表达式实际上比普通正则表达式更快,并且在处理错误值方面比异常快得多,这是有道理的。结果:

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

希望这是正确的 - 很想听听任何反例。 :)
F
FxIII

我做了一些速度测试。假设如果字符串可能是数字,则 try/except 策略是最快的。如果字符串不太可能是数字并且您对整数检查感兴趣,则值得进行一些测试(isdigit plus heading '-')。如果您有兴趣检查浮点数,则必须使用 try/except 代码来进行转义。


R
Russia Must Remove Putin

RyanN 建议

如果要为 NaN 和 Inf 返回 False,请将行更改为 x = float(s);返回 (x == x) 和 (x - 1 != x)。这应该为除 Inf 和 NaN 之外的所有浮点数返回 True

但这并不完全有效,因为对于足够大的浮点数,x-1 == x 返回 true。例如,2.0**54 - 1 == 2.0**54


K
Kobi

此代码处理指数、浮点数和整数,但不使用正则表达式。

return True if str1.lstrip('-').replace('.','',1).isdigit() or float(str1) else False

u
user1508746

我正在研究一个导致我进入这个线程的问题,即如何以最直观的方式将数据集合转换为字符串和数字。在阅读了原始代码后,我意识到我需要的内容在两个方面有所不同:

1 - 如果字符串表示整数,我想要一个整数结果

- 我希望将数字或字符串结果粘贴到数据结构中

所以我修改了原始代码来产生这个衍生物:

def string_or_number(s):
    try:
        z = int(s)
        return z
    except ValueError:
        try:
            z = float(s)
            return z
        except ValueError:
            return s

x
xin.chen
import re
def is_number(num):
    pattern = re.compile(r'^[-+]?[-0-9]\d*\.\d*|[-+]?\.?[0-9]\d*$')
    result = pattern.match(num)
    if result:
        return True
    else:
        return False


​>>>: is_number('1')
True

>>>: is_number('111')
True

>>>: is_number('11.1')
True

>>>: is_number('-11.1')
True

>>>: is_number('inf')
False

>>>: is_number('-inf')
False

你不认为 1e6 代表一个数字吗?
佚名

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

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

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


您应该将该函数的附加部分移动到 else 语句中,以避免在数组出现问题时意外触发异常。
m
mathfac

我也使用了您提到的函数,但很快我注意到字符串为“Nan”、“Inf”及其变体被视为数字。所以我建议你改进你的函数版本,这将在这些类型的输入上返回 false 并且不会失败“1e3”变体:

def is_float(text):
    try:
        float(text)
        # check for nan/infinity etc.
        if text.isalpha():
            return False
        return True
    except ValueError:
        return False

S
Samantha Atkins

用户辅助功能:

def if_ok(fn, string):
  try:
    return fn(string)
  except Exception as e:
    return None

然后

if_ok(int, my_str) or if_ok(float, my_str) or if_ok(complex, my_str)
is_number = lambda s: any([if_ok(fn, s) for fn in (int, float, complex)])

看起来很有趣,但请解释您的解决方案以及如何使用它,并提供一些输入和输出。
A
Amir Saniyan
def is_float(s):
    if s is None:
        return False

    if len(s) == 0:
        return False

    digits_count = 0
    dots_count = 0
    signs_count = 0

    for c in s:
        if '0' <= c <= '9':
            digits_count += 1
        elif c == '.':
            dots_count += 1
        elif c == '-' or c == '+':
            signs_count += 1
        else:
            return False

    if digits_count == 0:
        return False

    if dots_count > 1:
        return False

    if signs_count > 1:
        return False

    return True