2009-04-06 2 views
27

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

Суть этого упражнения вставить триплетных запятые в ряд так:

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 
} 
+0

По вашей ссылке, AS3 делает поддержку lookaheads. Найдите раздел по группам. – toolkit

+0

действительно? Хм пропустил это. Я посмотрю Спасибо. –

+0

Некоторый код, который очень хорошо работает для меня, размещен здесь http://www.cgiinteractive.com/blog/2009/05/formatting-numbers-with-commas-in-as3/ – jln646v

ответ

36

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

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

Следующие link предлагает AS3?

+3

Предпочитаю, что вы можете использовать lookbehinds: (? <= \ D) (? = (\ D {3}) + $) Таким образом, вы можете просто заменить на "," вместо замены на " \ 1 ". –

+1

Подтверждено, что это работает и в JavaScript. Браво. –

+0

Это самое красивое решение, которое я когда-либо видел. – iamwhitebox

2

Это действительно не лучшее использование регулярных выражений ... Я не знаю функции форматирования чисел, но 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 функцию замены, а затем отменить его обратно.

+0

У меня нет особой необходимости в решении RegEx, я больше задавался вопросом, как с ним можно обращаться с помощью регулярного выражения. Но похоже, что это не проблема проблемного регулярного выражения, особенно в случае: 100000000 =>, 100 000 000. Я не знаю, с чего начинать регулярное выражение с учетом –

+1

Но эта конкретная проблема * * может быть решена с помощью регулярного выражения и не меняет сначала строку. Ники и инструментарий показывают, как это делается. –

+0

@ Алан: Действительно, это можно сделать ... хотя, пожалуйста, не отстаивайте это! Говоря это, я думаю, что OP понимает, что это не очень подходящее использование RegEx. – Noldorin

1

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

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

Вот является implemntation 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"; 
} 
+0

Спасибо Chas, как POI, как бы я принял во внимание эту ситуацию: 100000000 =>, 100 000 000. Или это возможно с регулярным выражением? –

+0

Хм, отрицательный внешний вид с нулевой шириной сдвигает положение запятой и пытается сделать нормальное регулярное выражение с отрицательной обратной связью с нулевой шириной, работает только для групп, кратных трем. –

+0

Я думаю, что инструментарий имеет это с положительным взглядом с нулевой шириной –

7

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

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

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

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

+0

Великие умы думают одинаково ... +1 :-) – toolkit

+3

Для ActionScript вам нужно добавить флаг «g»/global: след («1234567» .replace (/ (\ d) (? = (\ d \ d \ d) + $)/g, "$ 1,")); – mikechambers

12

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

сообщества> зделение

Выкройка: /\d{1,3}(?=(\d{3})+(?!\d))/g

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

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

Он сделал работу!

+0

Как полезный совет для любого, кто смотрит в будущее, небольшое изменение в регулярном выражении чуть выше, чем я должен был выяснить: '/ \ d {1,3 } (? = (\ d {3}) + (? = \.))/g' Это будет форматировать высокоточные номера, такие как 4517534.24658 без добавления запятых _after_ десятичной. Это, конечно же, требует, чтобы число имело десятичное число в нем для правильной работы (что в моем случае оказалось правдой). :-) –

0

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

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

Вопрос о ActionScript, а не Java. –

3

Хотя многие из этих ответов отлично работают с положительными целыми числами, многие из их аргументов входов отлиты в цифрах, что подразумевает что они могут обрабатывать отрицательные значения или содержать десятичные числа, и здесь все решения терпят неудачу. Хотя выбранный в данный момент ответ не принимает номер, мне было любопытно найти решение, которое могло бы и было бы более эффективным, чем 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: ., output: "0." }, 
       { 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: -., output: "-0." }, 
       { 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." } 
      ]; 

      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.!== 21,987,654,321.
Test #53 Failed: -21,987,654,321.!== -21,987,654,321.

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

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

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() отвечает требованиям универсальности и за форма, хотя, конечно, не самая компактная или элегантная.

-1

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

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

Это регулярное выражение уже было предложено несколько раз (с небольшими, чисто косметическими отличиями). Но что вы делаете с этим циклом 'while'? Просто добавьте флаг 'g', как это делали другие респонденты, и пусть метод' replace' сделает это для вас. –

+0

Привет, да, вы правы. Я отредактировал свой ответ –

Смежные вопросы