【问题标题】:Insert commas into number string在数字字符串中插入逗号
【发布时间】:2010-10-17 19:17:38
【问题描述】:

您好,我正在尝试对字符串执行反向正则表达式搜索,以将其分成 3 位数字组。据我从 AS3 documentation 可以看出,在 reg ex 引擎中无法进行向后搜索。

这个练习的重点是在一个数字中插入三个逗号,如下所示:

10000000 => 10,000,000

我正在考虑这样做:

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

但这不正确,因为搜索不是从后面发生的,并且替换 $1 仅适用于第一个匹配项。

我感觉最好使用循环执行此任务。

更新:

由于 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
}

【问题讨论】:

  • 根据您的链接,AS3 确实支持前瞻。查找关于组的部分。
  • 真的吗?嗯错过了。我去看看谢谢
  • 一些对我很有效的代码已经发布在这里cgiinteractive.com/blog/2009/05/…
  • 如果您不只是为练习编写这样的代码,您可以使用内置的 NumberFormatter 类来格式化数字字符串。

标签: regex actionscript-3


【解决方案1】:

如果您的语言支持前瞻断言,那么我认为以下正则表达式将起作用:

(\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,"));    
    }    
}

【讨论】:

  • 我更喜欢这个,假设您可以使用lookbehinds: (?
【解决方案2】:

发现于http://gskinner.com/RegExr/

社区 > 千位分隔符

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

替换:$&,

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

它完成了工作!

【讨论】:

  • 作为对任何展望未来的人的有用提示,我必须弄清楚上面正则表达式的细微变化是:/\d{1,3}(?=(\d{3})+(?=\.))/g 这将格式化高精度数字,例如 4517534.24658不添加逗号小数。当然,这确实需要数字中有一个小数才能正常工作(在我的情况下恰好是这样)。 :-)
  • 您可以在原件前面加上否定的后视,(?<!\.),以阻止它在不需要小数的情况下使用逗号
【解决方案3】:

如果您的正则表达式引擎具有积极的前瞻性,您可以执行以下操作:

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

其中正向前瞻 (?=...) 表示正则表达式仅在前瞻表达式 ... 匹配时匹配。

(请注意,环视表达式并不总是非常有效。)

【讨论】:

  • 对于 ActionScript,您需要添加“g”/全局标志:trace("1234567".replace(/(\d)(?=(\d\d\d)+$) /g, "$1,"));
【解决方案4】:

虽然这些答案中的许多都适用于正整数,但它们的许多参数输入都被转换为数字,这意味着它们可以处理负值或包含小数,而这里所有的解决方案都失败了。虽然当前选择的答案没有假设一个数字,但我很想找到一个可以而且也比 RegExp 性能更高的解决方案(AS3 做得不好)。

我将许多答案放在一个测试课程中(并包括来自 blog 的解决方案和我自己的一个名为 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.128s 上也优于其他参数:

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() 可以满足多功能性和性能的要求,但肯定不是最紧凑或最优雅的。

【讨论】:

    【解决方案5】:

    这确实不是 RegEx 的最佳用途...我不知道数字格式化功能,但 this thread 似乎提供了一个解决方案。

    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 来处理它。但似乎这不是正则表达式本身的问题,尤其是在以下情况下:100000000 => ,100,000,000。我不知道从哪里开始考虑到这个正则表达式
    • 但是这个特殊的问题可以用一个正则表达式来解决,而不是先反转字符串。 Niki 和工具包展示了它是如何完成的。
    • @Alan:确实可以做到……但请不要提倡!这么说,我认为 OP 明白这不是非常适合使用 RegEx。
    • 但是,如果不是通过练习像这样的小而独立的问题,人们应该如何学习正则表达式呢?这是一个不错的小练习。
    • 我想确实如此,只要人们对它们的实用性持谨慎态度。那里没有对象,真的。尽管如此,还是有很多非常实用的正则表达式可以练习写作。
    【解决方案6】:

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

    【讨论】:

    • 谢谢 Chas,作为 POI,我将如何考虑这种情况:100000000 => ,100,000,000。或者这甚至可以通过正则表达式实现?
    • 嗯,零宽度的负向后视只会移动逗号的位置,并且尝试使用零宽度的负向前瞻性执行正常的正则表达式仅适用于倍数的组三。
    • 我认为工具包具有零宽度正向预测
    • 正如 Brian 指出的那样,如果第一组由三个数字组成,您的技术会在字符串的开头放置一个逗号。我会为一个数字添加一个积极的前瞻,以确保我仍在数字内:/(\d{3})(?=\d)/g
    • 谢谢大家,所以总的来说,正则表达式解决方案似乎走上了一条过于复杂的道路:D
    【解决方案7】:

    你可以考虑NumberFormatter

    【讨论】:

      【解决方案8】:

      我会因为不是所请求的语言而投反对票,但这种非正则表达式技术应该适用(我通过搜索“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;
      });
      

      结果:

      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

      【讨论】:

        【解决方案9】:

        // 这是一个简单的代码,它运行良好...:)

        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("");
            }
        }
        

        【讨论】:

        • 问题是关于 ActionScript,而不是 Java。
        【解决方案10】:

        如果你不能对正则表达式使用前瞻,你可以使用这个:

        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/) {}
        

        【讨论】:

          【解决方案11】:

          Perl RegExp 1 衬里:

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

          【讨论】:

            【解决方案12】:

            试试这个代码。它简单且性能最佳。

            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
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多