17

在我来到这个网站之前,我一直在寻找一个可以将逗号放在数字集中的短代码。

编码:

function addCommas(nStr)
{
    nStr += '';
    x = nStr.split('.');
    x1 = x[0];
    x2 = x.length > 1 ? '.' + x[1] : '';
    var rgx = /(\d+)(\d{3})/;
    while (rgx.test(x1)) {
        x1 = x1.replace(rgx, '$1' + ',' + '$2');
    }
    return x1 + x2;
}  

真的很棒。有这个示例组号码:

addCommas('83475934.89');  

会返回"83,475,934.89",但是当我阅读代码时,我希望它会返回,8,3,4,7,5,934.89但是这个网站解释说

\d+结合\d{3}将匹配一组 3 个数字,前面有任意数量的数字。这会欺骗搜索从右到左替换。

我很困惑。这段代码如何从右到左读取?$1另外,是什么$2意思?

4

6 回答 6

15

实际上不是从右到左阅读。真正发生的是它反复应用(\d+)(\d{3})模式(通过一个while循环)并替换,直到它不再与模式匹配。换句话说:

迭代 1:

x1 = 83475934.89
x1.replace((\d+)(\d{3}), '$1' + ',' + '$2');
x1 = 83475,934.89

迭代 2:

x1 = 83475,934.89
x1.replace((\d+)(\d{3}), '$1' + ',' + '$2');
x1 = 83,475,934.89

迭代 3:

x1 = 83,475,934.89
x1.replace((\d+)(\d{3}), '$1' + ',' + '$2');
// no match; end loop

编辑:

另外,1 美元和 2 美元是什么意思?

这些分别是对匹配组的反向(\d+)引用(\d{3})

这是学习正则表达式如何实际工作的一个很好的参考:
http ://www.regular-expressions.info/quickstart.html

于 2013-05-30T12:16:02.273 回答
8

它从右到左匹配,因为它使用贪婪模式匹配。这意味着它首先找到所有数字(\d+),然后尝试找到 \d{3}。例如,在数字 2421567.56 中,它将首先匹配直到 '.' 的数字。- 2431567 - 然后向后工作以匹配正则表达式下一部分中的下 3 位数字 (567)。它在循环中执行此操作,在 $1 和 $2 变量之间添加逗号。

$ 表示在正则表达式中用括号形成的匹配组,例如 (\d+) = $1 和 (\d{3}) = $2。通过这种方式,它可以轻松地在它们之间添加字符。

在下一次迭代中,贪婪匹配会在新创建的逗号处停止,并继续直到它无法匹配 > 3 个数字。

于 2013-05-30T12:20:59.783 回答
3

此解释在同一页面上进一步向下

代码说明:如果有小数点,则代码开始将字符串分成两部分(nStrnStrEnd )。在nStr上使用正则表达式来添加逗号。然后nStrEnd被添加回来。如果字符串没有临时删除nStrEnd,则正则表达式会将10.0004格式化为10.0,004

正则表达式解释\d+\d{3}组合将匹配一组 3 个数字,前面有任意数量的数字。这会欺骗搜索从右到左替换。

$1$2是从正则表达式中捕获的组匹配。您可以在Regex Tutorial上阅读有关此主题的更多信息。

于 2013-05-30T12:13:10.913 回答
2

该代码确实从右到左读取,它所做的是搜索最大的数字行,(\d+)后跟 3 个数字(\d{3})。$1 和 $2 分别是最大的数字行和 3 位数字。所以它将逗号放在它们之间,通过重复这个过程,它可以以这种方式解析它。

于 2013-05-30T12:15:20.687 回答
2

我写了一个正则表达式,它一次完成同样的事情:

/(?!\b)(\d{3}(?=(\d{3})*\b))/g

例如,在开始时尝试使用不同的数字:

var num = '1234567890123456';

for(var i = 1; i <= num.length; i++)
{
  console.log(num.slice(0, -i).replace(/(?!\b)(\d{3}(?=(\d{3})*\b))/g, ',$1'));
}


我将尝试在这里分解它:

现在忽略这一点 - 我会回到那个。

(?!\b) (\d{3}(?=(\d{3})*\b))


它仍然从左到右读取试图捕获 3 位数的块。这是捕获组。

(?!\b) (\d{3} (?=(\d{3})*\b) )


但是,在捕获组内部,它使用前瞻。

(?!\b)(\d{3} (?= (\d{3})*\b ) )


前瞻查找锚定到数字末尾的 3 位数字的任意倍数 - 终止边界。这会将捕获从 number 的右端对齐到 3 的倍数。这意味着它也适用于十进制数字(除非它们超过 3 个小数位,在这种情况下,它也会在其中放置逗号。它并不完美)。

(?!\b)(\d{3}(?= (\d{3})*\b ))


我遇到的问题是 JavaScript 不支持原子后视,因此,当数字是 3 位的倍数时,它匹配前 3 位数字并在数字的开头放置一个逗号。
你不能在 3 位数字匹配之前匹配一个字符而不放弃重复,所以我不得不使用匹配单词边界的否定前瞻。这有点与^在开始时放置相反。

(?!\b) (\d{3}(?=(\d{3})*$))


本质上,它会阻止表达式从字符串的开头匹配。
这会很糟糕。

于 2017-05-30T08:51:18.663 回答
0

只是颠倒了字符串

function addCommas(num_as_string) {
    // convert into string
    num_as_string+='';
    if (num_as_string === "") {
        return num_as_string;
    }
    /* add more test here*/
    // split the integer part and decimal part
    let [integer_part, decimal_part] = num_as_string.split('.');
    if (decimal_part === undefined) {
        decimal_part = '';
    } else {
        decimal_part = '.' + decimal_part;
    }

    return integer_part.split('').reverse().join('').match(/\d\d?\d?/g).reverse().map(v => v.split('').reverse().join('')).join(',') + decimal_part;
}

输出:

addCommas('83475934.89')
"83,475,934.89"

addCommas(83475934.89)
"83,475,934.89"

addCommas(83475934)
"83,475,934"

于 2020-07-11T04:39:41.337 回答