如何在Python中将货币字符串转换为浮点数?

63

我有一些表示特定货币格式的数字字符串,例如:

money="$6,150,593.22"

我想将这个字符串转换为数字

6150593.22

如何最好地实现这个目标?


也许可以使用 http://code.google.com/p/python-money/? - Stephen Gross
10个回答

87

试试这个:

from re import sub
from decimal import Decimal

money = '$6,150,593.22'
value = Decimal(sub(r'[^\d.]', '', money))

这种方法有其优点,因为它使用Decimal而不是float(在表示货币时更好),并且通过不硬编码特定的货币符号也避免了任何区域设置问题。


10
为了保留负数的减号,将value = Decimal(sub(r'[^\d\-.]', '', money))翻译为:value = Decimal(sub(r'[^\d\-.]', '', money)) - Dave
22
请注意,并非所有本地化环境都使用句点作为小数分隔符,因此这是一种过于简单化的做法,可能会给全球观众带来问题。 - Red15
“'[^(\d,.)]'”适用于更多的语言环境。 - Iuri Guilherme

17

如果您的本地设置正确,可以使用locale.atof,但仍需要手动去掉“$”:

>>> import locale
>>> locale.setlocale(locale.LC_ALL, 'en_US.UTF8')
'en_US.UTF8'
>>> money = "$6,150,593.22"
>>> locale.atof(money.strip("$"))
6150593.2199999997

2
在使用locale.atof时要加1分,但对于金融应用程序来说,float显然不是最佳选择。 - Fred Foo
2
它适用于en_US本地化。但是,例如,es_MX本地化会出现float()无效文字:6,150,593.22错误... - Javier Novoa C.
@JavierNovoaC。这个解决方案只适用于en_US语言环境。 - Iuri Guilherme

9

如果要实现一种不硬编码货币位置或符号的解决方案:

raw_price = "17,30 €"
import locale
locale.setlocale(locale.LC_ALL, 'fr_FR.UTF8')
conv = locale.localeconv()
raw_numbers = raw_price.strip(conv['currency_symbol'])
amount = locale.atof(raw_numbers)

conv['currency_symbol'].decode('utf-8') 对我来说失败了(" 'str' object has no attribute 'decode'), 但是这个不需要 decode 就可以运行。 - cphlewis

9
我发现babel非常有用,可以解决以下问题: 这使得在本地化的场景下轻松解析数字。
>>> babel.numbers.parse_decimal('1,024.64', locale='en')                                                                                                                           
Decimal('1024.64')
>>> babel.numbers.parse_decimal('1.024,64', locale='de')
Decimal('1024.64')
>>>

你可以使用 babel.numbers.get_currency_symbol('USD') 来获取货币符号,而不需要硬编码前缀/后缀。
祝好, dtk

6

扩展包括带括号的负数:

In [1]: import locale, string

In [2]: from decimal import Decimal

In [3]: n = ['$1,234.56','-$1,234.56','($1,234.56)', '$ -1,234.56']

In [4]: tbl = string.maketrans('(','-')

In [5]: %timeit -n10000 [locale.atof( x.translate(tbl, '$)')) for x in n]
10000 loops, best of 3: 31.9 æs per loop

In [6]: %timeit -n10000 [Decimal( x.translate(tbl, '$,)')) for x in n]
10000 loops, best of 3: 21 æs per loop

In [7]: %timeit -n10000 [float( x.replace('(','-').translate(None, '$,)')) for x in n]
10000 loops, best of 3: 3.49 æs per loop

In [8]: %timeit -n10000 [float( x.translate(tbl, '$,)')) for x in n]
10000 loops, best of 3: 2.19 æs per loop

请注意,float()/Decimal()中的逗号必须被去除。可以使用replace()或translate()与翻译表一起使用将开括号(转换为-。translate()速度稍快。float()速度最快,比其他方法快10-15倍,但缺乏精度,可能会出现本地化问题。Decimal()具有精度,并且比locale.atof()快50%,但也存在本地化问题。locale.atof()最慢,但最通用。
编辑:新的str.translate API(映射到None的字符从str.translate函数移动到翻译表中)。
In [1]: import locale, string
        from decimal import Decimal

        locale.setlocale(locale.LC_ALL, '')

        n = ['$1,234.56','-$1,234.56','($1,234.56)', '$ -1,234.56']

In [2]: tbl = str.maketrans('(', '-', '$)')
        %timeit -n10000 [locale.atof( x.translate(tbl)) for x in n]
18 µs ± 296 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [3]: tbl2 = str.maketrans('(', '-', '$,)')
        %timeit -n10000 [Decimal( x.translate(tbl2)) for x in n]
3.77 µs ± 50.8 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [4]: %timeit -n10000 [float( x.translate(tbl2)) for x in n]
3.13 µs ± 66.3 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [5]: tbl3 = str.maketrans('', '', '$,)')
        %timeit -n10000 [float( x.replace('(','-').translate(tbl3)) for x in n]
3.51 µs ± 84.8 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

2

在 @Andrew Clark 答案 的基础上进行拓展。

对于非 en_US 的其他语言环境:

>>> import re
>>> import locale
>>> locale.setlocale(locale.LC_NUMERIC, 'pt_BR.UTF8') # this is for atof()
'pt_BR.UTF8'
>>> locale.setlocale(locale.LC_MONETARY, 'pt_BR.UTF8') # this is for currency()
'pt_BR.UTF8'
>>> curr = locale.currency(6150593.22, grouping = True)
>>> curr
'R$ 6.150.593,22'
>>> re.sub('[^(\d,.)]', '', curr)
'15,00'
>>> locale.atof(re.sub('[^(\d,.)]', '', curr))
6150593.22
>>> 6150593.22 == locale.atof(re.sub('[^(\d,.)]', '', locale.currency(6150593.22, grouping = True)))
True

必须提醒的是:在Python中,货币的适当类型是Decimal,而不是浮点数。


0

我将提供我的解决方案,希望它能帮助那些不仅遇到 ,,而且还遇到 . 问题的人。

def process_currency_adaptive(currency_string: str, decimal_sep_char: str) -> float:
    """
    Converts the currency string to common float format:
        Format: 
            ######.### 
        Example: 
            6150593.22
    """
    # Get rid of currency symbol
    currency_symbols = ["$", "€", "£", "₺"]
    
    # Replace any occurrence of currency symbol with empty string
    for symbol in currency_symbols:
        currency_string = currency_string.replace(symbol, "")
    
    
    if decimal_sep_char == ",":
        triple_sep_char = "."
    elif decimal_sep_char == ".":
        triple_sep_char = ","
    else:
        raise ValueError("Invalid decimal separator character: {}".format(decimal_sep_char))

    # Get rid of the triple separator
    currency_string = currency_string.replace(triple_sep_char, "")
    
    # There should be only one decimal_sep_char.
    if currency_string.count(decimal_sep_char) != 1:
        print("Error: Invalid currency format with value: {}".format(currency_string))
        raise ValueError
    
    return float(currency_string.replace(decimal_sep_char, "."))

# test process_currency
print(process_currency_adaptive("942,695", decimal_sep_char=","))  # 942.695
print(process_currency_adaptive("$6,150,593.22", decimal_sep_char="."))  # 6150593.22        

0

我发现最简单的方法是不需要硬编码或混淆货币检测,而且使用Decimal类型可以避免float类型的问题:

>>> from decimal import Decimal
>>> money="$6,150,593.22"
>>> amount = Decimal("".join(d for d in money if d.isdigit() or d == '.'))
>>> amount
Decimal('6150593.22')

credit: https://www.reddit.com/r/learnpython/comments/2248mp/how_to_format_currency_without_currency_sign/cgjd1o4?utm_source=share&utm_medium=web2x

0

我几年前编写了这个函数以解决同样的问题。

def money(number):
    number = number.strip('$')
    try:
        [num,dec]=number.rsplit('.')
        dec = int(dec)
        aside = str(dec)
        x = int('1'+'0'*len(aside))
        price = float(dec)/x
        num = num.replace(',','')
        num = int(num)
        price = num + price
    except:
        price = int(number)
    return price

8
永远不要像这样使用裸的 except,否则会阻止使用CTRL-C等功能。 - Mark Lawrence

0

这个函数将土耳其货币格式转换为十进制数。

money = '1.234,75'
def make_decimal(string):
    result = 0
    if string:
        [num, dec] = string.rsplit(',')
        result += int(num.replace('.', ''))
        result += (int(dec) / 100)
    return result
print(make_decimal(money))
1234.75

谢谢您的回答,但它对于这种情况不起作用:make_decimal("942,695") # 返回 948.95。您可以看一下我的解决方案,它也适用于土耳其价格格式。 - Ibrahim Berber

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接