Insert commas into number string

Viewed 62136

Hey there, I'm trying to perform a backwards regular expression search on a string to divide it into groups of 3 digits. As far as i can see from the AS3 documentation, searching backwards is not possible in the reg ex engine.

The point of this exercise is to insert triplet commas into a number like so:

10000000 => 10,000,000

I'm thinking of doing it like so:

string.replace(/(\d{3})/g, ",$1")

But this is not correct due to the search not happening from the back and the replace $1 will only work for the first match.

I'm getting the feeling I would be better off performing this task using a loop.

UPDATE:

Due to AS3 not supporting lookahead this is how I have solved it.

public static function formatNumber(number:Number):String
{
    var numString:String = number.toString()
    var result:String = ''

    while (numString.length > 3)
    {
        var chunk:String = numString.substr(-3)
        numString = numString.substr(0, numString.length - 3)
        result = ',' + chunk + result
    }

    if (numString.length > 0)
    {
        result = numString + result
    }

    return result
}
12 Answers

If your language supports postive lookahead assertions, then I think the following regex will work:

(\d)(?=(\d{3})+$)

Demonstrated in Java:

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class CommifyTest {

    @Test
    public void testCommify() {
        String num0 = "1";
        String num1 = "123456";
        String num2 = "1234567";
        String num3 = "12345678";
        String num4 = "123456789";

        String regex = "(\\d)(?=(\\d{3})+$)";

        assertEquals("1", num0.replaceAll(regex, "$1,"));
        assertEquals("123,456", num1.replaceAll(regex, "$1,"));
        assertEquals("1,234,567", num2.replaceAll(regex, "$1,"));
        assertEquals("12,345,678", num3.replaceAll(regex, "$1,"));
        assertEquals("123,456,789", num4.replaceAll(regex, "$1,"));    
    }    
}

Found on http://gskinner.com/RegExr/

Community > Thousands separator

Pattern: /\d{1,3}(?=(\d{3})+(?!\d))/g

Replace: $&,

trace ( String("1000000000").replace( /\d{1,3}(?=(\d{3})+(?!\d))/g , "$&,") );

It done the job!

If your regex engine has positive lookaheads, you could do something like this:

string.replace(/(\d)(?=(\d\d\d)+$)/, "$1,")

Where the positive lookahead (?=...) means that the regex only matches when the lookahead expression ... matches.

(Note that lookaround-expressions are not always very efficient.)

This really isn't the best use of RegEx... I'm not aware of a number formatting function, but this thread seems to provide a solution.

function commaCoder(yourNum):String {
    //var yourNum:Number = new Number();
    var numtoString:String = new String();
    var numLength:Number = yourNum.toString().length;
    numtoString = "";

    for (i=0; i<numLength; i++) { 
        if ((numLength-i)%3 == 0 && i != 0) {
            numtoString += ",";
        }
        numtoString += yourNum.toString().charAt(i);
        trace(numtoString);
    }
    return numtoString;
}

If you really are insistent on using RegEx, you could just reverse the string, apply the RegEx replace function, then reverse it back.

A sexeger is good for this. In brief, a sexeger is a reversed regex run against a reversed string that you reverse the output of. It is generally more efficient than the alternative. Here is some pseudocode for what you want to do:

string = reverse string
string.replace(/(\d{3})(?!$)/g, "$1,")
string = reverse string

Here is is a Perl implemntation

#!/usr/bin/perl

use strict;
use warnings;

my $s = 13_456_789;

for my $n (1, 12, 123, 1234, 12345, 123456, 1234567) {
    my $s = reverse $n;
    $s =~ s/([0-9]{3})(?!$)/$1,/g;
    $s = reverse $s;
    print "$s\n";
}

I'll take the downvotes for not being the requested language, but this non-regex technique should apply (and I arrived here via searching for "C# regex to add commas into number")

var raw = "104241824    15202656 KB 13498560 KB 1612672KB already 1,000,000 or 99.999 or 9999.99";

int i = 0;
bool isnum = false;
var formatted = raw.Reverse().Aggregate(new StringBuilder(), (sb, c) => {
    //$"{i}: [{c}] {isnum}".Dump();
    
    if (char.IsDigit(c) && c != ' ' && c!= '.' && c != ',') {
        if (isnum) {
            if (i == 3) {
                //$"ins ,".Dump();
                sb.Insert(0, ',');
                i = 0;
            }
        }
        else isnum = true;
        i++;
    }
    else {
        isnum = false;
        i = 0;
    }
    
    sb.Insert(0, c);
    return sb;
});

results in:

104,241,824 15,202,656 KB 13,498,560 KB 1,612,672KB already 1,000,000 or 99.999 or 9,999.99

If you can't use lookahead on regular expressions, you can use this:

string.replace(/^(.*?,)?(\d{1,3})((?:\d{3})+)$/, "$1$2,$3")

inside a loop until there's nothing to replace.

For example, a perlish solution would look like this:

my $num = '1234567890';
while ($num =~ s/^(.*?,)?(\d{1,3})((?:\d{3})+)$/$1$2,$3/) {}

Perl RegExp 1 liner:

1 while $VAR{total} =~ s/(.*\d)(\d\d\d)/$1,$2/g;

Related