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


1605

检查字符串是否可以在Python中表示为数字的最佳方法是什么?

我目前拥有的功能是:

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

这不仅丑陋而且缓慢,看起来笨拙。但是我还没有找到更好的方法,因为调用floatmain函数甚至更糟。


61
您当前的解决方案出了什么问题?它简短,快速且可读。
上校恐慌

5
而且,您不必返回True或False。您可以返回经过适当修改的值-例如,您可以使用此值将非数字用引号引起来。
Thruston

7
如果转换成功,返回float的结果会更好吗?您仍然可以检查是否成功(结果为False),并且实际上已经进行了转换,无论如何您都希望进行转换。
Jiminion

8
即使这个问题比较老,我只是想说这是一种优雅的方式,已记录为EAFP。因此,可能是解决此类问题的最佳解决方案。
thiruvenkadam 2013年

7
失败时不要返回float的结果或None。如果您随后使用它,因为x = float('0.00'); if x: use_float(x);现在您的代码中有一个错误。真实的值是这些函数引发异常而不是None首先返回的原因。更好的解决方案是避免使用该实用程序功能,并在try catch需要使用时将其包围在float中。
ovangle

Answers:


698

不仅丑陋而且缓慢

我都对此表示怀疑。

正则表达式或其他字符串解析方法将更难看,更慢。

我不确定任何事情都可以比上述速度更快。它调用该函数并返回。“尝试/捕获”不会带来太多开销,因为无需对堆栈帧进行大量搜索即可捕获最常见的异常。

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

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

C(作为示例)通过多种方式进行破解。Python清楚明确地列出了它。

我认为您执行此操作的代码是完美的。


21
我认为代码不是完美的(但我认为它非常接近):通常将要“测试”的部分放在try子句中,因此我将return Truein 放在else子句中try。原因之一是对于问题中的代码,如果必须对其进行检查,则必须检查该try子句中的第二条语句不会引发ValueError:授予,这不需要太多时间或脑力,但是为什么在不需要时使用任何东西呢?
Eric O Lebigot

4
答案似乎很引人注目,但令我感到奇怪的是为什么没有立即提供它……我将在任何情况下复制并使用它。
sage 2013年

9
真可怕。如果我不在乎数字什么,那又如何呢?我不是以1行IsNumeric()结尾,而是以try / catch结束,或者以另一个包裹try / catch的结尾。恩
基本的

6
未“开箱即用”提供它,因为if is_number(s): x = float(x) else: // fail它与的行数相同try: x = float(x) catch TypeError: # fail。该效用函数是完全不必要的抽象。
ovangle

12
但是抽象是库的重点。拥有“ isNumber”功能(以任何语言显示)都将极大地帮助您,因为您可以将其直接构建到if语句中,并且具有更多的可读性和可维护性代码,这些代码依赖于try-catch块。同样,如果您需要在一个以上的类/模块中多次使用该代码,那么您所使用的代码行会比内置函数多。
JamEngulfer

1611

如果您正在寻找解析(正,无符号)整数而不是浮点数,则可以将该isdigit()函数用于字符串对象。

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

字符串方法- isdigit()Python2Python3

Unicode字符串上也有一些内容,我不太熟悉 Unicode-是十进制/十进制


232
这是在否定负以及
intrepion

22
也无法使用指数:'1e3'.isdigit()-> False
ssc

35
虽然Number!= Digit,但是正在寻找测试字符串是否包含整数的人们可能会很偶然地发现这个问题,而isDigit方法可能非常适合他们的应用。
亚当·帕金

8
@AdamParkin:isdigit()int()有不同意见关于什么是整数例如,对于Unicode字符u'\u00b9'u'¹'.isdigit()True,但int(u'¹')引发ValueError异常。
2012年

6
+1:isdigit()可能不是OP想要的,但这正是我想要的。答案和方法可能未必未涵盖所有类型的数字,但与它的准确性相反,它仍然具有很高的相关性。尽管“ Number!= Digit”,数字仍然是数字的子集,尤其是正数,非负数和以1-10为底的数字。此外,对于要检查字符串是否为数字ID(通常属于我刚刚描述的数字子集)的情况,此方法特别有用且简短。
贾斯汀·约翰逊

159

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

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

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()

在此处输入图片说明


14
获得漂亮的图表+1。我看到了基准和图表,所有的TL; DR事情都变得清晰直观。
jcchuks

我同意@JCChuks:该图对快速获取所有TL; DR很有帮助。但是我认为TL; DR(例如TL; DR:最好的解决方案是 s.replace('.','',1).isdigit())应该出现在此答案的开头。无论如何,它应该被接受。谢谢!
Simon C.

10
此方法不处理负数(破折号)。我主张只使用float方法,因为它不太容易出错,并且每次都会起作用。
Urchin

3
需要注意的重要一点是,即使在假设没有破折号的情况下,replace-isdigit方法也仅对非数字更快(假结果),而try-except方法对于数字更快(真结果)。如果您的大部分输入是有效输入,那么最好使用try-except解决方案!
Markus von Broady

1
不适用于像'1.5e-9'或否定的指数表示法。
EL_DON

68

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

如果要让is_number为'NaN'返回FALSE,则此代码将不起作用,因为Python将其转换为非数字的表示形式(谈论身份问题):

>>> float('NaN')
nan

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

G。


2
实际上,如果传递的文本实际上不是数字的表示形式,则NaN返回(而不是False)可能是一个很好的值。检查它是一种痛苦(Python的float类型确实需要一种方法),但是您可以在计算中使用它而不会产生错误,并且只需要检查结果即可。
kindall 2011年

7
另一个例外是字符串'inf'。无论是inf或者NaN也可以用一个前缀+-仍然被接受。
2012年

4
如果要为NaN和Inf返回False,请将行更改为x = float(s); 返回(x == x)和(x-1!= x)。对于Inf和NaN以外的所有浮动,这应该返回True
RyanN

5
x-1 == x对于小于的大浮点数是正确的inf。在Python 3.2中,您可以math.isfinite用来测试既不是NaN也不是无穷大的数字,或者在此之前math.isnanmath.isinf之后都进行检查。
史蒂夫·杰索普

56

这个怎么样:

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

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

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

将返回假

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


7
但是,这并不代表负数。
迈克尔·巴顿

5
或具有类似指数的数字1.234e56(也可以写成,+1.234E+56还有其他几种变体)。
Alfe 2016年

re.match(r'^[+-]*(0[xbo])?[0-9A-Fa-f]*\.?[0-9A-Fa-f]*(E[+-]*[0-9A-Fa-f]+)$', 'str')应该可以更好地确定一个数字(但不是全部,我不是这样认为的)。我不建议使用此代码,最好使用发问者的原始代码。
Baldrickk

如果您不喜欢此解决方案,请在投票之前阅读本内容
aloisdg移至codidact.com,

伙计,这是我在该网站上见过的最聪明的解决方案!伙计,伙计!
卡伦·库赛

41

不仅丑陋且缓慢,而且看起来笨拙。

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

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

其他语言通过使用接口来解决这些问题。我将保存对另一个线程更好的解决方案的分析。不过,要点是,Python绝对位于等式的鸭式输入端,如果您打算在Python中进行大量编程,则可能必须习惯使用这种语法(但这并不意味着您当然要喜欢它)。

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


1
Python将异常用于基本函数的另一个常见地方hasattr()是仅getattr()包装在中的调用try/except。但是,异常处理比正常的流控制要慢,因此在大多数情况下将其用于某些情况可能会导致性能下降。
kindall 2011年

看来,如果您想要
Basic

关于使用廉价异常的影响,pythonic的想法是“比请求更好的请求宽恕”。
heltonbiker '16

40

在Alfe指出您不需要单独检查float之后进行了更新,因为这两种情况都比较复杂:

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

14
我不同意。在正常使用中,这是非常不可能的,您最好在使用它们时为它建立一个is_complex_number()调用,而不是给调用增加额外的操作负担,以免出现0.0001%的错误操作。
Jiminion

3
您可以float()完全剥离内容,只需检查complex()呼叫是否成功即可。解析的所有内容都float()可以由解析complex()
Alfe

此函数将返回熊猫的NaN和Inf值作为数字值。
fixxxer

complex('(01989)')会回来的(1989+0j)。但是float('(01989)')会失败。所以我认为使用complex不是一个好主意。
plhn

26

为此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

2
不处理1.234e56类似的事情。另外,我很想知道您怎么会发现这99999999999999999999e99999999999999999999不是一个数字。尝试解析它很快就会发现。
Alfe

在50m字符串列表上,此方法比公认的解决方案快约30%,而在5k字符串列表上,则快150%。👏
泽夫Averbach

15

只是模仿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'类进行猴子补丁添加这些方法。

如果您想尊重现有功能,则代码应类似于:

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

SideNote:我个人更喜欢将其命名为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

伟大的贤者蟒蛇对罗马教廷神父说,“任何你能做的我都能做得更好;我能做的比你做得更好。”


我最近一直在使用JS进行编码,实际上并没有进行测试,因此可能存在一些小错误。如果您看到任何错误,请随时纠正我的错误。
埃文·普赖斯

要增加对复数的支持,请参阅@Matthew Wilcoxson的答案。stackoverflow.com/a/3335060/290340
埃文·普赖斯

1
使用!代替not可能是一个小错误,但是您绝对不能为floatCPython中的内置对象分配属性。
BlackJack,

15

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

如你看到的

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

我的立场是正确的:-}看起来好像不是在这样做。也许使用诸如prep_code_basisprep_code_re_method这样的名称可以避免我的错误。
Alfe

您介意至少在isfloat功能方面解释模块的工作方式吗?
所罗门·乌科

@SolomonUcko这是字符串检查部分的源代码链接:github.com/SethMMorton/fastnumbers/blob/v1.0.0/src/…。基本上,它按顺序遍历字符串中的每个字符,并验证它遵循有效浮点的模式。如果输入已经是数字,则仅使用快速PyFloat_Check
塞斯摩顿(SethMMorton)'16

1
经过对该线程中最佳替代方案的测试,我确认该解决方案是迄今为止最快的。第二快的方法是str(s).strip('-').replace('.','',1).isdigit()慢大约10倍!
亚历山大·麦克法兰

14

我知道这是特别古老的,但我想补充一个答案,我相信它涵盖了投票率最高的答案所缺少的信息,对于发现此问题的任何人可能都非常有价值:

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

x.isdigit() 非常适合检查x是否为整数。

x.replace('-','').isdigit() 对于检查x是否为负数效果很好(检查-在第一个位置)

x.replace('.','').isdigit() 非常适合检查x是否为小数。

x.replace(':','').isdigit() 非常适合检查x是否为比率。

x.replace('/','',1).isdigit() 非常适合检查x是否为分数。


1
尽管对于分数,您可能需要这样做x.replace('/','',1).isdigit(),否则诸如4/7/2017之类的日期将被误解为数字。
Chen

有关链接条件的最佳方法:stackoverflow.com/q/3411771/5922329
Daniel Braun

13

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

  • 正整数
  • 正/负-整数/浮点数
  • 在检查数字时如何丢弃“ 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”(不是数字)字符串

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

>>> is_number('NaN')
True

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

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

>>> math.isnan(nan_num)
True

或者,如果您不想导入其他库进行检查,则可以通过使用与自己进行比较来简单地进行检查==。Python中返回False时,nan浮子与自身相比。例如:

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

因此,上述功能is_number可以更新,返回False"NaN"是:

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适合您要求的功能版本。


12

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


11
您的“ 2e-2”美元也是浮动货币(使用float的附加参数:)
tzot

8
@tzot切勿使用浮点数表示货币值。
路加福音

6
@卢克:我完全同意你的观点,尽管我从未建议使用浮点数来表示货币价值。我只是说过,货币价值可以表示为浮点数:)
tzot


9

我想看看哪种方法最快。总体上,最佳和最一致的结果由该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

以下是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

10
您还应该测试无效案例的性能。这些数字没有引起异常,这恰好是“慢”的部分。
UgoMéda13年

1
@UgoMéda,我从2013年开始就采纳您的建议:)
Ron Reiter

“请注意,检查铸造是否成功是唯一准确的方法。” <-实际上并非如此。我在上面的答案中使用了regexp来运行您的测试,它实际上比regexp的运行速度更快。我将结果添加到上面的答案中。
David Ljung Madison Stellar

顺便说一句,有趣的是,您的坏号码创建者实际上可以创建一些合法号码,尽管这种情况很少见。:)
David Ljung Madison Stellar

8

因此,将它们放在一起,检查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

到目前为止最好的答案。谢谢
anish

6

输入可能如下:

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函数

查找给定变量是否为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-如果您确信变量内容为String

使用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”?

4

我做了一些速度测试。假设如果字符串可能是数字,则try / except策略可能是最快的方法。如果字符串不太可能是数字,并且您对Integer检查感兴趣,那么值得进行一些测试(等号加标题) '-')。如果您有兴趣检查浮点数,则必须使用try / except代码whitout转义。


4

我需要确定字符串是否转换为基本类型(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) 

3

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


3

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

这些答案似乎确实有很多正则表达式的讨厌之处,我认为这是不合理的,正则表达式可以合理地清洁,正确和快速。这实际上取决于您要执行的操作。最初的问题是如何“检查字符串是否可以表示为数字(浮点数)”(根据标题)。大概在检查完数字/浮点值的有效性之后,便会希望使用它。在这种情况下,您的try / except很有意义。但是,如果由于某种原因,您只想验证字符串是否为数字那么正则表达式也可以正常工作,但是很难正确。我认为到目前为止,大多数正则表达式答案都无法正确解析没有整数部分(例如“ .7”)的字符串,而整数部分就python而言是一个浮点数。在不需要小数部分的单个正则表达式中进行检查有点棘手。我已经包含了两个正则表达式来说明这一点。

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

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

无论如何,如果没有更多信息,我相信这是一个正则表达式,它涵盖了所有int和float,因为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的答案中运行基准测试代码表明,此regex实际上比普通regex快,并且处理异常值的速度也比异常快得多,这是有道理的。结果:

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

希望是对的-很想听听任何反例。:)
David Ljung麦迪逊·斯特拉19'May

2
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

2
您不考虑1e6代表数字吗?
马克·迪金森

1

这是我执行此操作的简单方法。假设我正在遍历一些字符串,并且如果它们最终是数字,我想将它们添加到数组中。

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

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


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

1

我还使用了您提到的函数,但是很快我注意到,字符串“ 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

1

该代码使用正则表达式处理指数,浮点数和整数。

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

1

用户助手功能:

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)])

0

您可以通过返回比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:]), ')'

0

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

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

2-我希望将数字或字符串结果插入数据结构

所以我修改了原始代码以生成此派生代码:

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


0
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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.