Analizar la moneda en números en Python

Acabo de enterarme de los números de Formato como moneda en Python que el módulo de Python babel proporciona babel.numbers.format_currency para dar formato a los números como moneda. Por ejemplo,

 from babel.numbers import format_currency s = format_currency(123456.789, 'USD', locale='en_US') # u'$123,456.79' s = format_currency(123456.789, 'EUR', locale='fr_FR') # u'123\xa0456,79\xa0\u20ac' 

¿Qué hay de lo contrario, de la moneda a los números, como $123,456,789.00 -> 123456789 ? babel proporciona babel.numbers.parse_number para analizar números locales, pero no encontré algo como parse_currency . Entonces, ¿cuál es la forma ideal de analizar la moneda local en números?


Pasé por Python: quitando caracteres, excepto los dígitos de la cadena .

 # Way 1 import string all=string.maketrans('','') nodigs=all.translate(all, string.digits) s = '$123,456.79' n = s.translate(all, nodigs) # 12345679, lost `.` # Way 2 import re n = re.sub("\D", "", s) # 12345679 

No se ocupa el separador decimal . .


Eliminar todos los caracteres no numéricos, a excepción de . , desde una cadena (refiérase aquí ),

 import re # Way 1: s = '$123,456.79' n = re.sub("[^0-9|.]", "", s) # 123456.79 # Way 2: non_decimal = re.compile(r'[^\d.]+') s = '$123,456.79' n = non_decimal.sub('', s) # 123456.79 

Se procesa el separador decimal . .


Pero las soluciones anteriores no funcionan cuando se trata de, por ejemplo,

 from babel.numbers import format_currency s = format_currency(123456.789, 'EUR', locale='fr_FR') # u'123\xa0456,79\xa0\u20ac' new_s = s.encode('utf-8') # 123 456,79 € 

Como puede ver, el formato de la moneda varía. ¿Cuál es la forma ideal de analizar la moneda en números de una manera general?

Usando babel

La documentación de babel señala que el análisis de números no está completamente implementado, sí, pero han hecho un gran trabajo para obtener información sobre la moneda en la biblioteca. Puede usar get_currency_name() y get_currency_symbol() para obtener detalles de la moneda, y también todas las demás funciones get_... para obtener los detalles del número normal (punto decimal, signo menos, etc.).

Usando esa información, puede excluir de una cadena de moneda los detalles de la moneda (nombre, signo) y agrupaciones (por ejemplo , en los Estados Unidos). Luego, cambia los detalles decimales a los que usa la configuración regional C ( - para menos, y . Para el punto decimal).

Esto da como resultado este código (agregué un objeto para conservar algunos de los datos, lo que puede ser útil en el procesamiento posterior):

 import re, os from babel import numbers as n from babel.core import default_locale class AmountInfo(object): def __init__(self, name, symbol, value): self.name = name self.symbol = symbol self.value = value def parse_currency(value, cur): decp = n.get_decimal_symbol() plus = n.get_plus_sign_symbol() minus = n.get_minus_sign_symbol() group = n.get_group_symbol() name = n.get_currency_name(cur) symbol = n.get_currency_symbol(cur) remove = [plus, name, symbol, group] for token in remove: # remove the pieces of information that shall be obvious value = re.sub(re.escape(token), '', value) # change the minus sign to a LOCALE=C minus value = re.sub(re.escape(minus), '-', value) # and change the decimal mark to a LOCALE=C decimal point value = re.sub(re.escape(decp), '.', value) # just in case remove extraneous spaces value = re.sub('\s+', '', value) return AmountInfo(name, symbol, value) #cur_loc = os.environ['LC_ALL'] cur_loc = default_locale() print('locale:', cur_loc) test = [ (n.format_currency(123456.789, 'USD', locale=cur_loc), 'USD') , (n.format_currency(-123456.78, 'PLN', locale=cur_loc), 'PLN') , (n.format_currency(123456.789, 'PLN', locale=cur_loc), 'PLN') , (n.format_currency(123456.789, 'IDR', locale=cur_loc), 'IDR') , (n.format_currency(123456.789, 'JPY', locale=cur_loc), 'JPY') , (n.format_currency(-123456.78, 'JPY', locale=cur_loc), 'JPY') , (n.format_currency(123456.789, 'CNY', locale=cur_loc), 'CNY') , (n.format_currency(-123456.78, 'CNY', locale=cur_loc), 'CNY') ] for v,c in test: print('As currency :', c, ':', v.encode('utf-8')) info = parse_currency(v, c) print('As value :', c, ':', info.value) print('Extra info :', info.name.encode('utf-8') , info.symbol.encode('utf-8')) 

La salida parece prometedora (en la configuración regional de EE. UU.):

 $ export LC_ALL=en_US $ ./cur.py locale: en_US As currency : USD : b'$123,456.79' As value : USD : 123456.79 Extra info : b'US Dollar' b'$' As currency : PLN : b'-z\xc5\x82123,456.78' As value : PLN : -123456.78 Extra info : b'Polish Zloty' b'z\xc5\x82' As currency : PLN : b'z\xc5\x82123,456.79' As value : PLN : 123456.79 Extra info : b'Polish Zloty' b'z\xc5\x82' As currency : IDR : b'Rp123,457' As value : IDR : 123457 Extra info : b'Indonesian Rupiah' b'Rp' As currency : JPY : b'\xc2\xa5123,457' As value : JPY : 123457 Extra info : b'Japanese Yen' b'\xc2\xa5' As currency : JPY : b'-\xc2\xa5123,457' As value : JPY : -123457 Extra info : b'Japanese Yen' b'\xc2\xa5' As currency : CNY : b'CN\xc2\xa5123,456.79' As value : CNY : 123456.79 Extra info : b'Chinese Yuan' b'CN\xc2\xa5' As currency : CNY : b'-CN\xc2\xa5123,456.78' As value : CNY : -123456.78 Extra info : b'Chinese Yuan' b'CN\xc2\xa5' 

Y todavía funciona en diferentes lugares (Brasil es notable por usar la coma como una marca decimal):

 $ export LC_ALL=pt_BR $ ./cur.py locale: pt_BR As currency : USD : b'US$123.456,79' As value : USD : 123456.79 Extra info : b'D\xc3\xb3lar americano' b'US$' As currency : PLN : b'-PLN123.456,78' As value : PLN : -123456.78 Extra info : b'Zloti polon\xc3\xaas' b'PLN' As currency : PLN : b'PLN123.456,79' As value : PLN : 123456.79 Extra info : b'Zloti polon\xc3\xaas' b'PLN' As currency : IDR : b'IDR123.457' As value : IDR : 123457 Extra info : b'Rupia indon\xc3\xa9sia' b'IDR' As currency : JPY : b'JP\xc2\xa5123.457' As value : JPY : 123457 Extra info : b'Iene japon\xc3\xaas' b'JP\xc2\xa5' As currency : JPY : b'-JP\xc2\xa5123.457' As value : JPY : -123457 Extra info : b'Iene japon\xc3\xaas' b'JP\xc2\xa5' As currency : CNY : b'CN\xc2\xa5123.456,79' As value : CNY : 123456.79 Extra info : b'Yuan chin\xc3\xaas' b'CN\xc2\xa5' As currency : CNY : b'-CN\xc2\xa5123.456,78' As value : CNY : -123456.78 Extra info : b'Yuan chin\xc3\xaas' b'CN\xc2\xa5' 

Vale la pena señalar que babel tiene algunos problemas de encoding. Esto se debe a que los archivos de configuración regional (en locale-data ) utilizan una encoding diferente. Si está trabajando con divisas con las que está familiarizado, eso no debería ser un problema. Pero si prueba monedas desconocidas, podría tener problemas (acabo de enterarme de que Polonia usa iso-8859-2 , no iso-8859-1 ).