Needed formatted numbers with thousands separator commas added on an end-user report. The usual way mentioned is to use 'module locale.format', but that didn't work cleanly on my Windows machine, and the cure seemed worse than the disease.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | import re
__test__ = {}
re_digits_nondigits = re.compile(r'\d+|\D+')
__test__['re_digits_nondigits'] = r"""
>>> re_digits_nondigits.findall('$1234.1234')
['$', '1234', '.', '1234']
>>> re_digits_nondigits.findall('1234')
['1234']
>>> re_digits_nondigits.findall('')
[]
"""
def FormatWithCommas(format, value):
"""
>>> FormatWithCommas('%.4f', .1234)
'0.1234'
>>> FormatWithCommas('%i', 100)
'100'
>>> FormatWithCommas('%.4f', 234.5678)
'234.5678'
>>> FormatWithCommas('$%.4f', 234.5678)
'$234.5678'
>>> FormatWithCommas('%i', 1000)
'1,000'
>>> FormatWithCommas('%.4f', 1234.5678)
'1,234.5678'
>>> FormatWithCommas('$%.4f', 1234.5678)
'$1,234.5678'
>>> FormatWithCommas('%i', 1000000)
'1,000,000'
>>> FormatWithCommas('%.4f', 1234567.5678)
'1,234,567.5678'
>>> FormatWithCommas('$%.4f', 1234567.5678)
'$1,234,567.5678'
>>> FormatWithCommas('%i', -100)
'-100'
>>> FormatWithCommas('%.4f', -234.5678)
'-234.5678'
>>> FormatWithCommas('$%.4f', -234.5678)
'$-234.5678'
>>> FormatWithCommas('%i', -1000)
'-1,000'
>>> FormatWithCommas('%.4f', -1234.5678)
'-1,234.5678'
>>> FormatWithCommas('$%.4f', -1234.5678)
'$-1,234.5678'
>>> FormatWithCommas('%i', -1000000)
'-1,000,000'
>>> FormatWithCommas('%.4f', -1234567.5678)
'-1,234,567.5678'
>>> FormatWithCommas('$%.4f', -1234567.5678)
'$-1,234,567.5678'
"""
parts = re_digits_nondigits.findall(format % (value,))
for i in xrange(len(parts)):
s = parts[i]
if s.isdigit():
parts[i] = _commafy(s)
break
return ''.join(parts)
def _commafy(s):
r = []
for i, c in enumerate(reversed(s)):
if i and (not (i % 3)):
r.insert(0, ',')
r.insert(0, c)
return ''.join(r)
|
The recipe works by adding commas to the first contiguous group of digits. It could fail with some odd-ball format that puts extra digits before the number.
Tags: text
Another solution. #this solution uses the re module and a single function.
import re def currency(amount): temp = "%.2f" % amount profile=re.compile(r"(\d)(\d\d\d[.,])") while 1: temp, count = re.subn(profile,r"\1,\2",temp) if not count: break return '$'+temp
if __name__ == "__main__": print currency(3458905.54) print currency(-49786002.40)
Using locale. FYI, the reason using locale didn't work is probably because you didn't set the locale first:
Counterintuitive, not well documented, non-Pythonic and just plain sucky, but there you go.
Reverse the digits, add commas after each group of three (except the last), then reverse the result.
Nice and simple, but only works for integers; split on '\.' and rejoin if you have a decimal point.
Here is a solution that does not use a regex:
Neat Luciano. So a full version for 2 decimal place numbers would be:
def splitthousands(s, sep=','):
sign='' rhs='' if s[0]=='-': sign='-' s=s[1:] if s.rfind('.')>0: rhs=s[-3:] s=s[:-3] if len(s) <= 3: return sign+s+rhs return sign+splitthousands(s[:-3], sep) + sep + s[-3:]+rhs
a generalized - and, by the way, internationalized - version of solutions proposed by Luciano and Alexander is the following:
where:
tSep is the thousands' separator, that in the United States is the comma, but, for instance, in France is the space and in Italy and in Germany is the dot;
dSep is the decimal separator, that in the United States is the dot, but in the other above mentioned countries is the comma. dSep has "None" as default value since integers haven't a decimal part.
The solution proposed by Luciano and Alexander needs a slight change to the fourth line, otherwise it drops the first digit before the decimal place (e.g. "12345.6" returns "1,234.6"). Here is an updated version:
Sorry. In my previous post I was actually referring to the solution proposed by Luca Dentis.
I'd suggest a further refinement. Since you are using a decimal separator variable, that should be used when searching for where to split into the right and left hand sides. Perhaps it would be a good idea to use a reasonable default for dSep and check if it's a decimal that way instead of using the "search for a dot." The whole point of parameterizing tSep and dSep was to allow for internationalization.
With support for negative numbers added:
Handle leading +
And maybe
should instead be
to handle more types that happen to look numeric when stringified, and also to work better on Python 3.x, which doesn't have long.
This version factors out the recursion to reduce the number of redundant checks. splitThousandsPosInt only handles strings of digits (or whatever Garbage In). splitThousands handles any number of leading spaces or signs, and stops at a decimal point. GIGO, of course. Pass in properly formatted numbers and it will work.
Removed tail recursion (helper function not needed) handled leading signs and garbage differently. (By the way, where is strspn? I could not find it. Did not look real hard tho')
random googlers: this is now built in to py3k (non-locale aware).