How to format a number with commas as thousands separators?

题意:如何将数字格式化为使用逗号作为千位分隔符?

问题背景:

I am trying to print an integer in JavaScript with commas as thousands separators. For example, I want to show the number 1234567 as "1,234,567". How would I go about doing this?

我试图在JavaScript中以逗号作为千位分隔符来打印一个整数。例如,我想将数字1234567显示为“1,234,567”。我应该怎么做呢?

Here is how I am doing it:

我是这样做的:

function numberWithCommas(x) {
    x = x.toString();
    var pattern = /(-?\d+)(\d{3})/;
    while (pattern.test(x))
        x = x.replace(pattern, "$1,$2");
    return x;
}

console.log(numberWithCommas(1000))

Is there a simpler or more elegant way to do it? It would be nice if it works with floats also, but that is not necessary. It does not need to be locale-specific to decide between periods and commas.

扫描二维码关注公众号,回复: 17480171 查看本文章

有没有更简单或更优雅的方法来做这件事?如果它也适用于浮点数那就更好了,但这并不是必需的。它不需要根据地区设置来决定是使用点还是逗号。

问题解决:

I used the idea from Kerry's answer, but simplified it since I was just looking for something simple for my specific purpose. Here is what I have:

我采用了Kerry回答中的思路,但根据我的特定需求对其进行了简化。以下是我所做的:

function numberWithCommas(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

function numberWithCommas(x) {
    return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
}

function test(x, expect) {
    const result = numberWithCommas(x);
    const pass = result === expect;
    console.log(`${pass ? "✓" : "ERROR ====>"} ${x} => ${result}`);
    return pass;
}

let failures = 0;
failures += !test(0,        "0");
failures += !test(100,      "100");
failures += !test(1000,     "1,000");
failures += !test(10000,    "10,000");
failures += !test(100000,   "100,000");
failures += !test(1000000,  "1,000,000");
failures += !test(10000000, "10,000,000");
if (failures) {
    console.log(`${failures} test(s) failed`);
} else {
    console.log("All tests passed");
}
.as-console-wrapper {
    max-height: 100% !important;
}


The regex uses 2 lookahead assertions:

这个正则表达式使用了两个前瞻断言(lookahead assertions):

  • a positive one to look for any point in the string that has a multiple of 3 digits in a row after it, 一个正向的前瞻断言,用于查找字符串中任何后面有连续三个数字倍数的位置,
  • a negative assertion to make sure that point only has exactly a multiple of 3 digits. The replacement expression puts a comma there.   一个负向前瞻断言,确保该点只有恰好是3的倍数的位数。替换表达式在那里放置一个逗号。

For example, if you pass it 123456789.01, the positive assertion will match every spot to the left of the 7 (since 789 is a multiple of 3 digits, 678 is a multiple of 3 digits, 567, etc.). The negative assertion checks that the multiple of 3 digits does not have any digits after it. 789 has a period after it so it is exactly a multiple of 3 digits, so a comma goes there. 678 is a multiple of 3 digits but it has a 9 after it, so those 3 digits are part of a group of 4, and a comma does not go there. Similarly for 567456789 is 6 digits, which is a multiple of 3, so a comma goes before that. 345678 is a multiple of 3, but it has a 9 after it, so no comma goes there. And so on. The \B keeps the regex from putting a comma at the beginning of the string.

例如,如果你向它传递123456789.01,正向断言将与7左侧的每个位置匹配(因为789是3位数的倍数,678是3位数的倍数,567等也是)。负向前瞻断言检查3位数的倍数后没有任何数字。789后面有一个小数点,所以它恰好是3位数的倍数,因此在那里放置一个逗号。678是3位数的倍数,但它后面有一个9,所以这3个数字是4位数组的一部分,因此不在那里放置逗号。567的情况类似。456789是6位数,也是3的倍数,所以在它前面放置一个逗号。345678是3的倍数,但它后面有一个9,因此不在那里放置逗号。以此类推。\B确保正则表达式不会在字符串的开头放置逗号。

@neu-rah mentioned that this function adds commas in undesirable places if there are more than 3 digits after the decimal point. If this is a problem, you can use this function:

@neu-rah 提到,如果小数点后有超过 3 位数字,这个函数会在不恰当的位置添加逗号。如果这是一个问题,你可以使用这个函数:

function numberWithCommas(x) {
    var parts = x.toString().split(".");
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    return parts.join(".");
}

function numberWithCommas(x) {
    var parts = x.toString().split(".");
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    return parts.join(".");
}

function test(x, expect) {
    const result = numberWithCommas(x);
    const pass = result === expect;
    console.log(`${pass ? "✓" : "ERROR ====>"} ${x} => ${result}`);
    return pass;
}

let failures = 0;
failures += !test(0              , "0");
failures += !test(0.123456       , "0.123456");
failures += !test(100            , "100");
failures += !test(100.123456     , "100.123456");
failures += !test(1000           , "1,000");
failures += !test(1000.123456    , "1,000.123456");
failures += !test(10000          , "10,000");
failures += !test(10000.123456   , "10,000.123456");
failures += !test(100000         , "100,000");
failures += !test(100000.123456  , "100,000.123456");
failures += !test(1000000        , "1,000,000");
failures += !test(1000000.123456 , "1,000,000.123456");
failures += !test(10000000       , "10,000,000");
failures += !test(10000000.123456, "10,000,000.123456");
if (failures) {
    console.log(`${failures} test(s) failed`);
} else {
    console.log("All tests passed");
}
.as-console-wrapper {
    max-height: 100% !important;
}

@t.j.crowder pointed out that now that JavaScript has lookbehind (support info), it can be solved in the regular expression itself:

@t.j.crowder 指出,既然 JavaScript 已经支持了正向肯定预查(lookbehind,支持信息),那么这个问题就可以在正则表达式本身中得到解决:

function numberWithCommas(x) {
    return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
}

function numberWithCommas(x) {
    return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
}

function test(x, expect) {
    const result = numberWithCommas(x);
    const pass = result === expect;
    console.log(`${pass ? "✓" : "ERROR ====>"} ${x} => ${result}`);
    return pass;
}

let failures = 0;
failures += !test(0,               "0");
failures += !test(0.123456,        "0.123456");
failures += !test(100,             "100");
failures += !test(100.123456,      "100.123456");
failures += !test(1000,            "1,000");
failures += !test(1000.123456,     "1,000.123456");
failures += !test(10000,           "10,000");
failures += !test(10000.123456,    "10,000.123456");
failures += !test(100000,          "100,000");
failures += !test(100000.123456,   "100,000.123456");
failures += !test(1000000,         "1,000,000");
failures += !test(1000000.123456,  "1,000,000.123456");
failures += !test(10000000,        "10,000,000");
failures += !test(10000000.123456, "10,000,000.123456");
if (failures) {
    console.log(`${failures} test(s) failed`);
} else {
    console.log("All tests passed");
}
.as-console-wrapper {
    max-height: 100% !important;
}

(?<!\.\d*) is a negative lookbehind that says the match can't be preceded by a . followed by zero or more digits. The negative lookbehind is faster than the split and join solution (comparison), at least in V8.

(?<!\.\d*) 是一个负向后发断言,它表示匹配项之前不能是 . 后跟零个或多个数字。与 split 和 join 的解决方案相比(对比而言),负向后发断言在 V8 引擎中至少更快一些。

猜你喜欢

转载自blog.csdn.net/suiusoar/article/details/143473185