Erlang Central

Commas in Numbers

Revision as of 23:15, 3 September 2006 by Bfulgham (Talk | contribs)

Problem

You need to format a number for printing with commas (or other locale-dependant marks) in the appropriate places. This is important for creating human-legible print copies of reports.

Solution

Use the Perl-compatible regular expression engine to insert the commas where needed. It's simplest to reverse the string prior to the regexp-pass so that we can hop over any decimal portion. Then reverse the string back on return.

partition_number(Number, Separator) ->
    SepChar = hd(Separator),
    NumStr = if
       is_integer(Number) -> hd(io_lib:format("~B", [Number]));
       is_float(Number) -> hd(io_lib:format("~f", [Number]));
       true -> Number
    end,
    % If there is a decimal point, use that as the starting point
    {ChgStr, Remain} = case regexp:first_match(NumStr, "\\.") of
        {match,Start,Length} ->
       {string:substr(NumStr, 1, Start - 1),
                string:substr(NumStr,Start,string:len(NumStr))};
   nomatch -> {NumStr, ""}
    end,
    {ok, Subs, _} = regexp:gsub(lists:reverse(ChgStr),
       "([0-9][0-9][0-9])", "&" ++ Separator),
    Final = case lists:nth(string:len(Subs), Subs) of
        SepChar -> string:substr(Subs, 1, string:len(Subs) - 1);
        _       -> Subs
    end,
    lists:reverse(Final) ++ Remain.

While this function works, it would be better if it checked for someone passing the separator parameter as a character (rather than a string).

Here's an example of partition_number in action:

1> partition_number(1918928282, ",").
"1,918,928,282"
2> partition_number(1982928828, "'"). 
"1'982'928'828"
3> partition_number(1982928828.12345, "'").
"1'982'928'828.123450"

In addition to Erlang's various other string-related ills (mainly poor memory and time performance), it has a very limited set of regular expressions. For example, our implementation of partition_number is over twice as long as the [[1][Scheme alternative] because it has no look-ahead (or negative look-ahead) searching functionality.