Вставьте запятые в числовую строку - PullRequest
33 голосов
/ 06 апреля 2009

Привет, я пытаюсь выполнить обратный поиск по регулярному выражению в строке, чтобы разделить ее на группы из 3 цифр. Насколько я вижу из документации AS3 , поиск в обратном направлении невозможен в reg ex engine.

Смысл этого упражнения - вставить тройку запятых в число, например:

10000000 => 10,000,000

Я думаю сделать это так:

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

Но это не правильно из-за того, что поиск не происходит сзади, а замена $ 1 будет работать только для первого совпадения.

У меня такое чувство, что мне было бы лучше выполнить эту задачу с помощью цикла.

UPDATE:

Из-за того, что AS3 не поддерживает прогноз, я решил это так.

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
}

Ответы [ 10 ]

47 голосов
/ 06 апреля 2009

Если ваш язык поддерживает положительные косвенные утверждения, то, я думаю, сработает следующее регулярное выражение:

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

Демонстрируется на 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,"));    
    }    
}

Следующая ссылка предполагает, что AS3 делает?

16 голосов
/ 29 января 2011

найдено на http://gskinner.com/RegExr/

Сообщество> Разделитель тысяч

Шаблон: /\d{1,3}(?=(\d{3})+(?!\d))/g

Заменить: $&,

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

Сделано!

9 голосов
/ 06 апреля 2009

Если ваш движок регулярных выражений имеет положительные взгляды, вы можете сделать что-то вроде этого:

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

Где положительный прогноз (? = ...) означает, что регулярное выражение совпадает только тогда, когда выражение прогнозирования ... соответствует.

(Обратите внимание, что выражения lookaround не всегда очень эффективны.)

4 голосов
/ 16 ноября 2012

Хотя многие из этих ответов прекрасно работают с положительными целыми числами, многие из их входных аргументов приводятся в виде чисел, что означает, что они могут обрабатывать отрицательные значения или содержать десятичные дроби, и здесь все решения терпят неудачу. Хотя выбранный в настоящее время ответ не предполагает номер, мне было любопытно найти решение, которое могло бы и было бы более производительным, чем RegExp (что AS3 не очень хорошо).

Я собрал многие ответы здесь в классе тестирования (и включил решение из этого блога и мой собственный ответ commaify ) и отформатировал их в соответствии способ для простого сравнения:

package
{
    public class CommaNumberSolutions
    {   
        public static function commaify( input:Number ):String
        {
            var split:Array = input.toString().split( '.' ),
                front:String = split[0],
                back:String = ( split.length > 1 ) ? "." + split[1] : null,
                n:int = input < 0 ? 2 : 1,
                commas:int = Math.floor( (front.length - n) / 3 ),
                i:int = 1;

            for ( ; i <= commas; i++ )
            {
                n = front.length - (3 * i + i - 1);
                front = front.slice( 0, n ) + "," + front.slice( n );
            }

            if ( back )
                return front + back;
            else
                return front;
        }

        public static function getCommaString( input:Number ):String
        {
            var s:String = input.toString();

            if ( s.length <= 3 )
                return s;

            var i:int = s.length % 3;

            if ( i == 0 )
                i = 3;

            for ( ; i < s.length; i += 4 )
            {
                var part1:String = s.substr(0, i);
                var part2:String = s.substr(i, s.length);
                s = part1.concat(",", part2);
            }

            return s;
        }

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

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

            if ( s.length > 0 )
                result = s + result

            return result
        }

        public static function commaCoder( input:Number ):String
        {
            var s:String = "";
            var len:Number = input.toString().length;

            for ( var i:int = 0; i < len; i++ )
            { 
                if ( (len-i) % 3 == 0 && i != 0)
                    s += ",";

                s += input.toString().charAt(i);
            }
            return s;
        }

        public static function regex1( input:Number ):String
        {
            return input.toString().replace( /-{0,1}(\d)(?=(\d\d\d)+$)/g, "$1," );
        }

        public static function regex2( input:Number ):String
        {
            return input.toString().replace( /-{0,1}\d{1,3}(?=(\d{3})+(?!\d))/g , "$&,")
        }

        public static function addCommas( input:Number ):String
        {
            var negative:String = "";
            if ( input < 0 )
            {
                negative = "-";
                input = Math.abs(input);
            }

            var s:String = input.toString();
            var results:Array = s.split(/\./);
            s = results[0];

            if ( s.length > 3 )
            {
                var mod:Number = s.length % 3;
                var output:String = s.substr(0, mod);
                for ( var i:Number = mod; i < s.length; i += 3 )
                {
                    output += ((mod == 0 && i == 0) ? "" : ",") + s.substr(i, 3);
                }

                if ( results.length > 1 )
                {
                    if ( results[1].length == 1 )
                        return negative + output + "." + results[1] + "0";
                    else
                        return negative + output + "." + results[1];
                }
                else
                    return negative + output;
            }
            if ( results.length > 1 )
            {
                if ( results[1].length == 1 )
                    return negative + s + "." + results[1] + "0";
                else
                    return negative + s + "." + results[1];
            }
            else
                return negative + s;
        }
    }
}

Затем я проверил каждый на точность и производительность:

package
{    
    public class TestCommaNumberSolutions
    {
        private var functions:Array;

        function TestCommaNumberSolutions()
        {
            functions = [
                { name: "commaify()", f: CommaNumberSolutions.commaify },
                { name: "addCommas()", f: CommaNumberSolutions.addCommas },
                { name: "getCommaString()", f: CommaNumberSolutions.getCommaString },
                { name: "formatNumber()", f: CommaNumberSolutions.formatNumber },
                { name: "regex1()", f: CommaNumberSolutions.regex1 },
                { name: "regex2()", f: CommaNumberSolutions.regex2 },
                { name: "commaCoder()", f: CommaNumberSolutions.commaCoder }
            ];
            verify();
            measure();
        }

        protected function verify():void
        {
            var assertions:Array = [ 
                { input: 1, output: "1" },
                { input: 21, output: "21" },
                { input: 321, output: "321" },
                { input: 4321, output: "4,321" },
                { input: 54321, output: "54,321" },
                { input: 654321, output: "654,321" },
                { input: 7654321, output: "7,654,321" },
                { input: 987654321, output: "987,654,321" },
                { input: 1987654321, output: "1,987,654,321" },
                { input: 21987654321, output: "21,987,654,321" },
                { input: 321987654321, output: "321,987,654,321" },
                { input: 4321987654321, output: "4,321,987,654,321" },
                { input: 54321987654321, output: "54,321,987,654,321" },
                { input: 654321987654321, output: "654,321,987,654,321" },
                { input: 7654321987654321, output: "7,654,321,987,654,321" },
                { input: 87654321987654321, output: "87,654,321,987,654,321" },
                { input: -1, output: "-1" },
                { input: -21, output: "-21" },
                { input: -321, output: "-321" },
                { input: -4321, output: "-4,321" },
                { input: -54321, output: "-54,321" },
                { input: -654321, output: "-654,321" },
                { input: -7654321, output: "-7,654,321" },
                { input: -987654321, output: "-987,654,321" },
                { input: -1987654321, output: "-1,987,654,321" },
                { input: -21987654321, output: "-21,987,654,321" },
                { input: -321987654321, output: "-321,987,654,321" },
                { input: -4321987654321, output: "-4,321,987,654,321" },
                { input: -54321987654321, output: "-54,321,987,654,321" },
                { input: -654321987654321, output: "-654,321,987,654,321" },
                { input: -7654321987654321, output: "-7,654,321,987,654,321" },
                { input: -87654321987654321, output: "-87,654,321,987,654,321" },
                { input: .012345, output: "0.012345" },
                { input: 1.012345, output: "1.012345" },
                { input: 21.012345, output: "21.012345" },
                { input: 321.012345, output: "321.012345" },
                { input: 4321.012345, output: "4,321.012345" },
                { input: 54321.012345, output: "54,321.012345" },
                { input: 654321.012345, output: "654,321.012345" },
                { input: 7654321.012345, output: "7,654,321.012345" },
                { input: 987654321.012345, output: "987,654,321.012345" },
                { input: 1987654321.012345, output: "1,987,654,321.012345" },
                { input: 21987654321.012345, output: "21,987,654,321.012345" },
                { input: -.012345, output: "-0.012345" },
                { input: -1.012345, output: "-1.012345" },
                { input: -21.012345, output: "-21.012345" },
                { input: -321.012345, output: "-321.012345" },
                { input: -4321.012345, output: "-4,321.012345" },
                { input: -54321.012345, output: "-54,321.012345" },
                { input: -654321.012345, output: "-654,321.012345" },
                { input: -7654321.012345, output: "-7,654,321.012345" },
                { input: -987654321.012345, output: "-987,654,321.012345" },
                { input: -1987654321.012345, output: "-1,987,654,321.012345" },
                { input: -21987654321.012345, output: "-21,987,654,321.012345" }
            ];

            var i:int;
            var len:int = assertions.length;
            var assertion:Object;
            var f:Function;
            var s1:String;
            var s2:String;

            for each ( var o:Object in functions )
            {
                i = 0;
                f = o.f;
                trace( '\rVerify: ' + o.name ); 
                for ( ; i < len; i++ )
                {
                    assertion = assertions[ i ];
                    s1 = f.apply( null, [ assertion.input ] );
                    s2 = assertion.output;
                    if ( s1 !== s2 )
                        trace( 'Test #' + i + ' Failed: ' + s1 + ' !== ' + s2 );
                }
            }

        }

        protected function measure():void
        {
            // Generate random inputs
            var values:Array = [];
            for ( var i:int = 0; i < 999999; i++ ) {
                values.push( Math.random() * int.MAX_VALUE * ( Math.random() > .5 ? -1 : 1) );
            }

            var len:int = values.length;
            var stopwatch:Stopwatch = new Stopwatch;
            var s:String;
            var f:Function;
            trace( '\rTesting ' + len + ' random values' );
            // Test each function
            for each ( var o:Object in functions )
            {
                i = 0;
                s = "";
                f = o.f;
                stopwatch.start();
                for ( ; i < len; i++ ) {
                    s += f.apply( null, [ values[i] ] ) + " ";
                }
                stopwatch.stop();
                trace( o.name + '\t\ttook ' + (stopwatch.elapsed/1000) + 's' ); //(stopwatch.elapsed/len) + 'ms'
            }
        }
    }
}

import flash.utils.getTimer;

class Stopwatch
{
    protected var startStamp:int;
    protected var stopStamp:int;
    protected var _started:Boolean;
    protected var _stopped:Boolean;

    function Stopwatch( startNow:Boolean = true ):void
    {
        if ( startNow ) 
            start();
    }

    public function start():void
    {
        startStamp = getTimer();
        _started = true;
        _stopped = false;
    }

    public function stop():void
    {
        stopStamp = getTimer();
        _stopped = true;
        _started = false;
    }

    public function get elapsed():int
    {
        return ( _stopped ) ? stopStamp - startStamp : ( _started ) ? getTimer() - startStamp : 0;
    }

    public function get started():Boolean
    {
        return _started;
    }

    public function get stopped():Boolean
    {
        return _stopped;
    }
}

Из-за недостаточной точности AS3 с большими числами каждый класс не прошел эти тесты:

Test #15 Failed: 87,654,321,987,654,320 !== 87,654,321,987,654,321
Test #31 Failed: -87,654,321,987,654,320 !== -87,654,321,987,654,321
Test #42 Failed: 21,987,654,321.012344 !== 21,987,654,321.012345
Test #53 Failed: -21,987,654,321.012344 !== -21,987,654,321.012345

Но только две функции прошли все остальные тесты: commaify () и addCommas () .

Тесты производительности показывают, что commaify () является самым предварительным из всех решений:

Testing 999999 random values
commaify()        took 12.411s
addCommas()       took 17.863s
getCommaString()  took 18.519s
formatNumber()    took 14.409s
regex1()          took 40.654s
regex2()          took 36.985s

Кроме того, commaify () может быть расширен за счет включения аргументов для десятичной длины и дополнения нулями в десятичной части - он также превосходит другие при 13.128 с :

public static function cappedDecimal( input:Number, decimalPlaces:int = 2 ):Number
{
    if ( decimalPlaces == 0 ) 
        return Math.floor( input );

    var decimalFactor:Number = Math.pow( 10, decimalPlaces );

    return Math.floor( input * decimalFactor ) / decimalFactor;
}

public static function cappedDecimalString( input:Number, decimalPlaces:int = 2, padZeros:Boolean = true ):String
{
    if ( padZeros )
        return cappedDecimal( input, decimalPlaces ).toFixed( decimalPlaces );
    else
        return cappedDecimal( input, decimalPlaces ).toString();
}

public static function commaifyExtended( input:Number, decimalPlaces:int = 2, padZeros:Boolean = true ):String
{
   var split:Array = cappedDecimalString( input, decimalPlaces, padZeros ).split( '.' ),
       front:String = split[0],
       back:String = ( split.length > 1 ) ? "." + split[1] : null,
       n:int = input < 0 ? 2 : 1,
       commas:int = Math.floor( (front.length - n) / 3 ),
       i:int = 1;

   for ( ; i <= commas; i++ )
   {
       n = front.length - (3 * i + i - 1);
       front = front.slice( 0, n ) + "," + front.slice( n );
   }

   if ( back )
       return front + back;
   else
       return front;
}

Итак, я бы предложил, чтобы commaify () отвечала требованиям универсальности и производительности, хотя, конечно, не самая компактная или элегантная.

2 голосов
/ 06 апреля 2009

A sexeger хорош для этого. Короче говоря, sexeger - это обратное регулярное выражение, работающее с обратной строкой, для которой вы меняете вывод. Как правило, это более эффективно, чем альтернатива. Вот некоторый псевдокод для того, что вы хотите сделать:

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

Вот реализация Perl

#!/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";
}
2 голосов
/ 06 апреля 2009

Это на самом деле не лучшее использование RegEx ... Я не знаю о функции форматирования чисел, но этот поток , кажется, обеспечивает решение.

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;
}

Если вы действительно настаиваете на использовании RegEx, вы можете просто перевернуть строку, применить функцию замены RegEx, а затем повернуть ее обратно.

1 голос
/ 13 июня 2013

Вы можете рассмотреть NumberFormatter

0 голосов
/ 22 мая 2018

Если вы не можете использовать lookahead для регулярных выражений, вы можете использовать это:

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

внутри цикла, пока не за что заменить.

Например, решение perlish будет выглядеть так:

my $num = '1234567890';
while ($num =~ s/^(.*?,)?(\d{1,3})((?:\d{3})+)$/$1$2,$3/) {}
0 голосов
/ 18 октября 2014

Попробуйте этот код. это просто и лучшая производительность.

var reg:RegExp=/\d{1,3}(?=(\d{3})+(?!\d))/g;
var my_num:Number = 48712694;
var my_num_str:String = String(my_num).replace(reg,"$&,");
trace(my_num_str);

:: Выход ::

48,712,694
0 голосов
/ 30 августа 2012

// Это простой код, и он отлично работает ...:)

import java.util.Scanner;

public class NumberWithCommas {

    public static void main(String a[]) {
        Scanner sc = new Scanner(System.in);

        String num;

        System.out.println("\n enter the number:");

        num = sc.next();

        printNumber(num);
    }

    public static void printNumber(String ar) {
        int len, i = 0, temp = 0;
        len = ar.length();
        temp = len / 3;
        if (len % 3 == 0)
            temp = temp - 1;
        len = len + temp;
        char[] ch = ar.toCharArray();
        char[] ch1 = new char[len];
        for (int j = 0, k = (ar.length() - 1); j < len; j++)
        {
            if (i < 3)
            {
                ch1[j] = ch[k];
                i++;
                k--;
            }
            else
            {
                ch1[j] = ',';
                i = 0;
            }
        }
        for (int j = len - 1; j >= 0; j--)
            System.out.print(ch1[j]);
        System.out.println("");
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...