簡體   English   中英

檢查字符串是否可以在 Python 中轉換為浮點數

[英]Checking if a string can be converted to float in Python

我有一些 Python 代碼,它們通過字符串列表運行,並盡可能將它們轉換為整數或浮點數。 對整數執行此操作非常容易

if element.isdigit():
  newelement = int(element)

浮點數更難。 現在我正在使用partition('.')來拆分字符串並檢查以確保一側或兩側都是數字。

partition = element.partition('.')
if (partition[0].isdigit() and partition[1] == '.' and partition[2].isdigit()) 
    or (partition[0] == '' and partition[1] == '.' and partition[2].isdigit()) 
    or (partition[0].isdigit() and partition[1] == '.' and partition[2] == ''):
  newelement = float(element)

這行得通,但顯然 if 語句有點熊。 我考慮的另一個解決方案是將轉換包裝在 try/catch 塊中,看看它是否成功,如本問題所述。

有人有其他想法嗎? 關於分區和 try/catch 方法的相對優點的意見?

我只會用..

try:
    float(element)
except ValueError:
    print "Not a float"

..這很簡單,而且有效。 請注意,如果元素是例如 1<<1024,它仍然會拋出 OverflowError。

另一種選擇是正則表達式:

import re
if re.match(r'^-?\d+(?:\.\d+)$', element) is None:
    print "Not float"

檢查浮點數的 Python 方法:

def is_float(element: Any) -> bool:
    try:
        float(element)
        return True
    except ValueError:
        return False

始終進行單元測試。 什么是浮動,什么不是浮動可能會讓您感到驚訝:

Command to parse                        Is it a float?  Comment
--------------------------------------  --------------- ------------
print(isfloat(""))                      False
print(isfloat("1234567"))               True 
print(isfloat("NaN"))                   True        nan is also float
print(isfloat("NaNananana BATMAN"))     False
print(isfloat("123.456"))               True
print(isfloat("123.E4"))                True
print(isfloat(".1"))                    True
print(isfloat("1,234"))                 False
print(isfloat("NULL"))                  False       case insensitive
print(isfloat(",1"))                    False           
print(isfloat("123.EE4"))               False           
print(isfloat("6.523537535629999e-07")) True
print(isfloat("6e777777"))              True        This is same as Inf
print(isfloat("-iNF"))                  True
print(isfloat("1.797693e+308"))         True
print(isfloat("infinity"))              True
print(isfloat("infinity and BEYOND"))   False
print(isfloat("12.34.56"))              False       Two dots not allowed.
print(isfloat("#56"))                   False
print(isfloat("56%"))                   False
print(isfloat("0E0"))                   True
print(isfloat("x86E0"))                 False
print(isfloat("86-5"))                  False
print(isfloat("True"))                  False       Boolean is not a float.   
print(isfloat(True))                    True        Boolean is a float
print(isfloat("+1e1^5"))                False
print(isfloat("+1e1"))                  True
print(isfloat("+1e1.3"))                False
print(isfloat("+1.3P1"))                False
print(isfloat("-+1"))                   False
print(isfloat("(1)"))                   False       brackets not interpreted
'1.43'.replace('.','',1).isdigit()

僅當有一個或沒有“。”時才會返回true 在數字串中。

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

將返回false

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

將返回false

TL;博士

  • 如果您的輸入主要是可以轉換為浮點數的字符串,那么try: except:方法是最好的原生 Python 方法。
  • 如果您的輸入主要是無法轉換為浮點數的字符串,則正則表達式或分區方法會更好。
  • 如果您 1) 不確定您的輸入或需要更快的速度,並且 2) 不介意並且可以安裝第三方 C 擴展,那么 fastnumbers可以很好地工作。

通過名為fastnumbers的第三方模塊還有另一種方法可用(披露,我是作者); 它提供了一個名為isfloat的函數。 我在這個答案中采用了 Jacob Gabrielson 概述的 unittest 示例,但添加了fastnumbers.isfloat方法。 我還應該注意,Jacob 的示例對正則表達式選項不公平,因為由於點運算符,該示例中的大部分時間都花在全局查找中......我已經修改了該函數以提供更公平的比較來try: except: .


def is_float_try(str):
    try:
        float(str)
        return True
    except ValueError:
        return False

import re
_float_regexp = re.compile(r"^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$").match
def is_float_re(str):
    return True if _float_regexp(str) else False

def is_float_partition(element):
    partition=element.partition('.')
    if (partition[0].isdigit() and partition[1]=='.' and partition[2].isdigit()) or (partition[0]=='' and partition[1]=='.' and partition[2].isdigit()) or (partition[0].isdigit() and partition[1]=='.' and partition[2]==''):
        return True
    else:
        return False

from fastnumbers import isfloat


if __name__ == '__main__':
    import unittest
    import timeit

    class ConvertTests(unittest.TestCase):

        def test_re_perf(self):
            print
            print 're sad:', timeit.Timer('ttest.is_float_re("12.2x")', "import ttest").timeit()
            print 're happy:', timeit.Timer('ttest.is_float_re("12.2")', "import ttest").timeit()

        def test_try_perf(self):
            print
            print 'try sad:', timeit.Timer('ttest.is_float_try("12.2x")', "import ttest").timeit()
            print 'try happy:', timeit.Timer('ttest.is_float_try("12.2")', "import ttest").timeit()

        def test_fn_perf(self):
            print
            print 'fn sad:', timeit.Timer('ttest.isfloat("12.2x")', "import ttest").timeit()
            print 'fn happy:', timeit.Timer('ttest.isfloat("12.2")', "import ttest").timeit()


        def test_part_perf(self):
            print
            print 'part sad:', timeit.Timer('ttest.is_float_partition("12.2x")', "import ttest").timeit()
            print 'part happy:', timeit.Timer('ttest.is_float_partition("12.2")', "import ttest").timeit()

    unittest.main()

在我的機器上,輸出是:

fn sad: 0.220988988876
fn happy: 0.212214946747
.
part sad: 1.2219619751
part happy: 0.754667043686
.
re sad: 1.50515985489
re happy: 1.01107215881
.
try sad: 2.40243887901
try happy: 0.425730228424
.
----------------------------------------------------------------------
Ran 4 tests in 7.761s

OK

如您所見,正則表達式實際上並不像最初看起來那么糟糕,如果您真的需要速度, fastnumbers方法是相當不錯的。

只是為了多樣化,這是另一種方法。

>>> all([i.isnumeric() for i in '1.2'.split('.',1)])
True
>>> all([i.isnumeric() for i in '2'.split('.',1)])
True
>>> all([i.isnumeric() for i in '2.f'.split('.',1)])
False

編輯:我確信它不會支持所有的浮動情況,尤其是當有指數時。 為了解決這個問題,它看起來像這樣。 這將返回 True,只有 val 是浮點數,而 False 表示 int,但性能可能不如正則表達式。

>>> def isfloat(val):
...     return all([ [any([i.isnumeric(), i in ['.','e']]) for i in val],  len(val.split('.')) == 2] )
...
>>> isfloat('1')
False
>>> isfloat('1.2')
True
>>> isfloat('1.2e3')
True
>>> isfloat('12e3')
False

函數is_digit(str)的簡化版本,在大多數情況下就足夠了(不考慮指數符號“NaN”值):

def is_digit(str):
    return str.lstrip('-').replace('.', '').isdigit()

如果您關心性能(我不建議您這樣做),那么基於嘗試的方法顯然是贏家(與基於分區的方法或正則表達式方法相比),只要您不期望很多無效字符串,在這種情況下它可能會變慢(可能是由於異常處理的成本)。

再說一次,我不是建議你關心性能,只是給你數據,以防你每秒執行 100 億次,或者其他什么。 此外,基於分區的代碼不處理至少一個有效字符串。

$ ./floatstr.py
F..
partition sad: 3.1102449894
partition happy: 2.09208488464
..
re sad: 7.76906108856
re happy: 7.09421992302
..
try sad: 12.1525540352
try happy: 1.44165301323
.
======================================================================
FAIL: test_partition (__main__.ConvertTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./floatstr.py", line 48, in test_partition
    self.failUnless(is_float_partition("20e2"))
AssertionError

----------------------------------------------------------------------
Ran 8 tests in 33.670s

FAILED (failures=1)

這是代碼(Python 2.6,正則表達式取自 John Gietzen 的答案):

def is_float_try(str):
    try:
        float(str)
        return True
    except ValueError:
        return False

import re
_float_regexp = re.compile(r"^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$")
def is_float_re(str):
    return re.match(_float_regexp, str)


def is_float_partition(element):
    partition=element.partition('.')
    if (partition[0].isdigit() and partition[1]=='.' and partition[2].isdigit()) or (partition[0]=='' and partition[1]=='.' and pa\
rtition[2].isdigit()) or (partition[0].isdigit() and partition[1]=='.' and partition[2]==''):
        return True

if __name__ == '__main__':
    import unittest
    import timeit

    class ConvertTests(unittest.TestCase):
        def test_re(self):
            self.failUnless(is_float_re("20e2"))

        def test_try(self):
            self.failUnless(is_float_try("20e2"))

        def test_re_perf(self):
            print
            print 're sad:', timeit.Timer('floatstr.is_float_re("12.2x")', "import floatstr").timeit()
            print 're happy:', timeit.Timer('floatstr.is_float_re("12.2")', "import floatstr").timeit()

        def test_try_perf(self):
            print
            print 'try sad:', timeit.Timer('floatstr.is_float_try("12.2x")', "import floatstr").timeit()
            print 'try happy:', timeit.Timer('floatstr.is_float_try("12.2")', "import floatstr").timeit()

        def test_partition_perf(self):
            print
            print 'partition sad:', timeit.Timer('floatstr.is_float_partition("12.2x")', "import floatstr").timeit()
            print 'partition happy:', timeit.Timer('floatstr.is_float_partition("12.2")', "import floatstr").timeit()

        def test_partition(self):
            self.failUnless(is_float_partition("20e2"))

        def test_partition2(self):
            self.failUnless(is_float_partition(".2"))

        def test_partition3(self):
            self.failIf(is_float_partition("1234x.2"))

    unittest.main()

如果您不需要擔心數字的科學或其他表達式,並且只使用可能是帶句點或不帶句點的數字的字符串:

功能

def is_float(s):
    result = False
    if s.count(".") == 1:
        if s.replace(".", "").isdigit():
            result = True
    return result

Lambda 版本

is_float = lambda x: x.replace('.','',1).isdigit() and "." in x

例子

if is_float(some_string):
    some_string = float(some_string)
elif some_string.isdigit():
    some_string = int(some_string)
else:
    print "Does not convert to int or float."

這樣您就不會意外地將應該是 int 的內容轉換為 float。

此正則表達式將檢查科學浮點數:

^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$

但是,我相信您最好的選擇是嘗試使用解析器。

我使用了已經提到的函數,但很快我注意到字符串為“Nan”、“Inf”及其變體被視為數字。 因此,我建議您改進該函數的版本,該版本將在這些類型的輸入上返回 false 並且不會使“1e3”變體失敗:

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

您可以使用try - except - else子句,這將捕獲當傳遞的值無法轉換為浮點數時引發的任何轉換/值錯誤


  def try_parse_float(item):
      result = None
      try:
        float(item)
      except:
        pass
      else:
        result = float(item)
      return result

一個簡單的函數,無需嘗試和除操作即可獲得數字類型

def number_type(number):
    if number.isdigit():
        return int(number)
    elif number.replace(".","").isdigit():
        return float(number)
    else:
        return(type(number))

我一直在尋找一些類似的代碼,但看起來使用 try/excepts 是最好的方法。 這是我正在使用的代碼。 如果輸入無效,它包括重試功能。 我需要檢查輸入是否大於 0,如果是,則將其轉換為浮點數。

def cleanInput(question,retry=False): 
    inputValue = input("\n\nOnly positive numbers can be entered, please re-enter the value.\n\n{}".format(question)) if retry else input(question)
    try:
        if float(inputValue) <= 0 : raise ValueError()
        else : return(float(inputValue))
    except ValueError : return(cleanInput(question,retry=True))


willbefloat = cleanInput("Give me the number: ")

嘗試轉換為浮動。 如果有錯誤,打印 ValueError 異常。

try:
    x = float('1.23')
    print('val=',x)
    y = float('abc')
    print('val=',y)
except ValueError as err:
    print('floatErr;',err)

輸出:

val= 1.23
floatErr: could not convert string to float: 'abc'

將字典作為參數傳遞,它將轉換可以轉換為浮點數的字符串並保留其他字符串

def covertDict_float(data):
        for i in data:
            if data[i].split(".")[0].isdigit():
                try:
                    data[i] = float(data[i])
                except:
                    continue
        return data

我嘗試了上面一些簡單的選項,圍繞轉換為浮點數進行了嘗試測試,發現大多數回復都存在問題。

簡單測試(按照上述答案):

entry = ttk.Entry(self, validate='key')
entry['validatecommand'] = (entry.register(_test_num), '%P')

def _test_num(P):
    try: 
        float(P)
        return True
    except ValueError:
        return False

問題出現在:

  • 您輸入“-”以開始一個負數:

然后您嘗試float('-')失敗

  • 您輸入一個數字,然后嘗試刪除所有數字

然后你正在嘗試float('')這同樣也失敗了

我的快速解決方案是:

def _test_num(P):
    if P == '' or P == '-': return True
    try: 
        float(P)
        return True
    except ValueError:
        return False

似乎很多正則表達式都會錯過一件事或另一件事。 到目前為止,這一直對我有用:

(?i)^\s*[+-]?(?:inf(inity)?|nan|(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)\s*$

它允許帶有符號、nan、小數點前沒有數字和前導/尾隨空格(如果需要)的無窮大(或 inf)。 需要^$以防止將1.2f-2部分匹配為1.2

如果您需要解析一些將D用於雙精度科學記數法的文件,則可以使用[ed]而不是僅使用e 您可能想在之后替換它,或者只是在檢查之前替換它們,因為float()函數不允許這樣做。

我找到了一種可行的方法。 需要驗證這一點。 第一次在這里放東西。

def isfloat(a_str):
    try:
        x=float(a_str)
        if x%1 == 0:
            return False
        elif x%1 != 0: #an else also do
            return True
    except Exception as error:
            return False

這就像一個魅力:

[dict([a,int(x) if isinstance(x, str)
 and x.isnumeric() else float(x) if isinstance(x, str)
 and x.replace('.', '', 1).isdigit() else x] for a, x in json_data.items())][0]

我已經編寫了自己的函數。 我使用 floatN() 或 floatZ() 而不是 float(value)。 如果不能將值轉換為浮點數,則返回 None 或 0.0。 我將它們保存在我稱為 safeCast 的模塊中。

def floatN(value):
    try:
        if value is not None:
            fvalue = float(value)
        else:
            fvalue = None
    except ValueError:
        fvalue = None

    return fvalue


def floatZ(value):
    try:
        if value is not None:
            fvalue = float(value)
        else:
            fvalue = 0.0
    except ValueError:
        fvalue = 0.0

    return fvalue

在其他模塊中,我導入它們

from safeCasts import floatN, floatZ

然后使用 floatN(value) 或 floatZ(value) 而不是 float()。 顯然,您可以將此技術用於您需要的任何強制轉換功能。

這是一個簡單但有趣的問題。 下面提出的解決方案對我來說很好:

import re

val = "25,000.93$"

regex = r"\D"

splitted = re.split(regex, val)
splitted = list(filter(str.isdecimal, splitted))

if splitted:
    if len(splitted) > 1:
        splitted.insert(-1, ".")

    try:
        f = float("".join(splitted))
        print(f, "is float.")
        
    except ValueError:
        print("Not a float.")
        
else:
    print("Not a float.")

重要說明:此解決方案基於splitted中的最后一個值包含小數位的假設。

您可以創建一個函數 isfloat(),並用 isdigit() 代替整數和浮點數,但不能像您期望的那樣使用字符串。

a = raw_input('How much is 1 share in that company? \n')

def isfloat(num):
    try:
        float(num)
        return True
    except:
        return False
       
while not isfloat(a):
    print("You need to write a number!\n")
    a = raw_input('How much is 1 share in that company? \n')

我們可以將正則表達式用作: import re if re.match('[0-9]*.?[0-9]+', <your_string>): print("Its a float/int") else: print("Its something alien")讓我用英語解釋正則表達式,

  • * -> 0 次或多次出現
  • + -> 1 次或多次出現
  • ? -> 0/1 出現

現在,讓我們轉換

  • '[0-9]* -> 讓 0-9 之間出現 0 個或多個數字
  • \.? -> 后跟一個 0 或一個 '.'(如果您需要檢查它是否可以是 int/float 否則我們也可以使用代替 ?,使用 {1})
  • [0-9]+ -> 后跟 0-9 之間出現 0 個或多個數字
str(strval).isdigit()

似乎很簡單。

處理存儲為字符串或 int 或 float 的值

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM