题 如何使用逗号作为千位分隔符在JavaScript中打印数字


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

我是这样做的:

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

有更简单或更优雅的方式吗?如果它也适用于浮点数会很好,但这不是必需的。它不需要特定于语言环境来决定句点和逗号。


1171
2018-05-24 23:42


起源


数(X).toLocaleString() - Boffin
@Boffin这不适用于输入类型编号(因为逗号) - 在接受的答案中,我们可以将逗号替换为点,输入类型编号将起作用 - Vitaly Zdanevich
值得注意的是 Number.prototype.toLocaleString  仍然 在Safari中不起作用 2016。它不是实际格式化数字,而是返回它,不会抛出任何错误。今天有了最大的facepalm ... #goodworkApple - aendrew
但是,它确实可以在iOS Safari中使用。 - Tim
toLocaleString不一致,不应使用。例如 - 在Firefox上,这将返回1,234,但在IE上,这将添加小数:1,234.00 - Bort


答案:


我使用了克里答案的想法,但简化了它,因为我只是为了我的特定目的而寻找简单的东西。这是我做的:

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

这是你真正需要知道的。

@Neils Bom询问了正则表达式的工作原理。我的解释有点长。它不适合评论,我不知道在哪里放,所以我在这里做。如果有人有任何其他建议,请告诉我。

正则表达式使用2个前瞻断言:一个正向查找字符串中的任何一个点,后面有一个3位数的倍数,以及一个负断言,以确保该点只有3个数字的倍数。替换表达式在那里放一个逗号。

例如,如果你传递它“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提到如果小数点后面有3位以上的数字,这个函数会在不受欢迎的地方添加逗号。如果这是一个问题,您可以使用此功能:

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

2072
2018-05-25 00:40



这太棒了!感谢您将Kerry的答案提炼到我需要的地方。谢谢!对于我最初的工作,这是一个更优雅的解决方案。 - Mike Grace
很酷,确实注意到它在小数点后面有超过3个位置的数字有问题。 - Eric Petroelje
尝试numberWithCommas(12345.6789) - >“12,345.6,789”我不喜欢它 - neu-rah
在“。”之后修复的小改进。问题'123456789.01234'.replace(/ \ B(?=(?= \ d * \。)(\ d {3})+(?!\ d))/ g,'_') - Dmitrij Golubev
@DmitrijGolubev对整数不起作用。也许强制小数点就是解决方案。 - Vlad


我很惊讶没有人提到过 Number.prototype.toLocaleString。 它是在JavaScript 1.5(1999年推出)中实现的,因此基本上支持主流浏览器。

var n = 34523453.345
n.toLocaleString()
"34,523,453.345"

它也可以通过包含在v0.12中的Node.js中运行 国际

如果你想要不同的东西, Numeral.js 可能很有趣。


1052
2017-07-15 21:01



@csigrist好点,但它没有看起来那么糟糕。速度取决于浏览器。在FF或Opera中表现良好。我虽然糟透了。至于零: var number = 123456.000; number.toLocaleString('en-US', {minimumFractionDigits: 2}); "123,456.00" 但这些选项在FF或Safari中不起作用。 - uKolka
似乎在Safari中也不起作用。 - lambinator
根据具体情况,性能差异可能是也可能不是问题。如果用于1000个结果的巨型表格,那么它将更重要,但如果仅用于单个值,则差异可以忽略不计。但优点是它具有区域设置意识,因此欧洲有人会看到 34.523.453,345 要么 34 523 453,345。这对于来自许多国家/地区的访问者的网站来说更为重要。 - T Nguyen
+1为数字.js - Brade
谷歌的更新: toLocaleString 从v0.12开始在Node.js中工作 包含国际。 - srobinson


var number = 1234567890; // Example number to be converted

 请注意,javascript有一个 最大整数 的价值 9007199254740991


的toLocaleString

number.toLocaleString(); // "1,234,567,890"

// A more complex example: 
var number2 = 1234.56789; // floating point example
number2.toLocaleString(undefined, {maximumFractionDigits:2}) // "1,234.57"


的NumberFormat (苹果浏览器 不支持):

var nf = new Intl.NumberFormat();
nf.format(number); // "1,234,567,890"

根据我的检查(至少是Firefox),它们在性能方面或多或少相同。


177
2017-08-22 08:52



对于那些在那里实施的人,Safari不支持NumberFormat。 - marcovega
在safari上也不支持toLocaleString - Deepak Banka
浏览器支持始终在每个MDN页面的底部提及,我已链接到该页面。 - vsync
基本 toLocaleString 在野生动物园工作,选项没有 - dandavis
该 toLocaleString 解决方案应该还包括所需的语言环境,所以 toLocaleString("en"),因为英文模式使用逗号。但是,如果 toLocaleString() 没有语言环境指示符在法国运行,那么它将产生句号而不是逗号,因为这是用于在本地分离数千个。 - Mike 'Pomax' Kamermans


我建议使用phpjs.org的 number_format()

function number_format(number, decimals, dec_point, thousands_sep) {
    // http://kevin.vanzonneveld.net
    // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +     bugfix by: Michael White (http://getsprink.com)
    // +     bugfix by: Benjamin Lupton
    // +     bugfix by: Allan Jensen (http://www.winternet.no)
    // +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +     bugfix by: Howard Yeend
    // +    revised by: Luke Smith (http://lucassmith.name)
    // +     bugfix by: Diogo Resende
    // +     bugfix by: Rival
    // +      input by: Kheang Hok Chin (http://www.distantia.ca/)
    // +   improved by: davook
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +      input by: Jay Klehr
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +      input by: Amir Habibi (http://www.residence-mixte.com/)
    // +     bugfix by: Brett Zamir (http://brett-zamir.me)
    // +   improved by: Theriault
    // +   improved by: Drew Noakes
    // *     example 1: number_format(1234.56);
    // *     returns 1: '1,235'
    // *     example 2: number_format(1234.56, 2, ',', ' ');
    // *     returns 2: '1 234,56'
    // *     example 3: number_format(1234.5678, 2, '.', '');
    // *     returns 3: '1234.57'
    // *     example 4: number_format(67, 2, ',', '.');
    // *     returns 4: '67,00'
    // *     example 5: number_format(1000);
    // *     returns 5: '1,000'
    // *     example 6: number_format(67.311, 2);
    // *     returns 6: '67.31'
    // *     example 7: number_format(1000.55, 1);
    // *     returns 7: '1,000.6'
    // *     example 8: number_format(67000, 5, ',', '.');
    // *     returns 8: '67.000,00000'
    // *     example 9: number_format(0.9, 0);
    // *     returns 9: '1'
    // *    example 10: number_format('1.20', 2);
    // *    returns 10: '1.20'
    // *    example 11: number_format('1.20', 4);
    // *    returns 11: '1.2000'
    // *    example 12: number_format('1.2000', 3);
    // *    returns 12: '1.200'
    var n = !isFinite(+number) ? 0 : +number, 
        prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
        sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
        dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
        toFixedFix = function (n, prec) {
            // Fix for IE parseFloat(0.55).toFixed(0) = 0;
            var k = Math.pow(10, prec);
            return Math.round(n * k) / k;
        },
        s = (prec ? toFixedFix(n, prec) : Math.round(n)).toString().split('.');
    if (s[0].length > 3) {
        s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
    }
    if ((s[1] || '').length < prec) {
        s[1] = s[1] || '';
        s[1] += new Array(prec - s[1].length + 1).join('0');
    }
    return s.join(dec);
}

更新02/13/14

人们一直在报道这不能按预期工作,所以我做了一个 JS小提琴 包括自动化测试。

更新于26/11/2017

这里的小提琴作为一个稍微修改输出的Stack Snippet:

function number_format(number, decimals, dec_point, thousands_sep) {
    // http://kevin.vanzonneveld.net
    // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +     bugfix by: Michael White (http://getsprink.com)
    // +     bugfix by: Benjamin Lupton
    // +     bugfix by: Allan Jensen (http://www.winternet.no)
    // +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +     bugfix by: Howard Yeend
    // +    revised by: Luke Smith (http://lucassmith.name)
    // +     bugfix by: Diogo Resende
    // +     bugfix by: Rival
    // +      input by: Kheang Hok Chin (http://www.distantia.ca/)
    // +   improved by: davook
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +      input by: Jay Klehr
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +      input by: Amir Habibi (http://www.residence-mixte.com/)
    // +     bugfix by: Brett Zamir (http://brett-zamir.me)
    // +   improved by: Theriault
    // +   improved by: Drew Noakes
    // *     example 1: number_format(1234.56);
    // *     returns 1: '1,235'
    // *     example 2: number_format(1234.56, 2, ',', ' ');
    // *     returns 2: '1 234,56'
    // *     example 3: number_format(1234.5678, 2, '.', '');
    // *     returns 3: '1234.57'
    // *     example 4: number_format(67, 2, ',', '.');
    // *     returns 4: '67,00'
    // *     example 5: number_format(1000);
    // *     returns 5: '1,000'
    // *     example 6: number_format(67.311, 2);
    // *     returns 6: '67.31'
    // *     example 7: number_format(1000.55, 1);
    // *     returns 7: '1,000.6'
    // *     example 8: number_format(67000, 5, ',', '.');
    // *     returns 8: '67.000,00000'
    // *     example 9: number_format(0.9, 0);
    // *     returns 9: '1'
    // *    example 10: number_format('1.20', 2);
    // *    returns 10: '1.20'
    // *    example 11: number_format('1.20', 4);
    // *    returns 11: '1.2000'
    // *    example 12: number_format('1.2000', 3);
    // *    returns 12: '1.200'
    var n = !isFinite(+number) ? 0 : +number, 
        prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
        sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
        dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
        toFixedFix = function (n, prec) {
            // Fix for IE parseFloat(0.55).toFixed(0) = 0;
            var k = Math.pow(10, prec);
            return Math.round(n * k) / k;
        },
        s = (prec ? toFixedFix(n, prec) : Math.round(n)).toString().split('.');
    if (s[0].length > 3) {
        s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
    }
    if ((s[1] || '').length < prec) {
        s[1] = s[1] || '';
        s[1] += new Array(prec - s[1].length + 1).join('0');
    }
    return s.join(dec);
}

var exampleNumber = 1;
function test(expected, number, decimals, dec_point, thousands_sep)
{
    var actual = number_format(number, decimals, dec_point, thousands_sep);
    console.log(
        'Test case ' + exampleNumber + ': ' +
        '(decimals: ' + (typeof decimals === 'undefined' ? '(default)' : decimals) +
        ', dec_point: "' + (typeof dec_point === 'undefined' ? '(default)' : dec_point) + '"' +
        ', thousands_sep: "' + (typeof thousands_sep === 'undefined' ? '(default)' : thousands_sep) + '")'
    );
    console.log('  => ' + (actual === expected ? 'Passed' : 'FAILED') + ', got "' + actual + '", expected "' + expected + '".');
    exampleNumber++;
}

test('1,235',    1234.56);
test('1 234,56', 1234.56, 2, ',', ' ');
test('1234.57',  1234.5678, 2, '.', '');
test('67,00',    67, 2, ',', '.');
test('1,000',    1000);
test('67.31',    67.311, 2);
test('1,000.6',  1000.55, 1);
test('67.000,00000', 67000, 5, ',', '.');
test('1',        0.9, 0);
test('1.20',     '1.20', 2);
test('1.2000',   '1.20', 4);
test('1.200',    '1.2000', 3);
.as-console-wrapper {
  max-height: 100% !important;
}


86
2018-05-24 23:47



不是我把它标记下来,但我相信人们这样做是因为它不起作用。就目前而言,我甚至发现你的一些测试无法正常工作。 - Andrew S
@Andrew S - 只有一个人将其标记下来。它确实有效,我已经多次在我自己的代码中使用它。它也不是我的代码(也不是我的测试),我引用了它来自的网站,这是一个众所周知的网站。也许他们有更新版本)因为您正在查看的代码是3年。 - Kerry Jones
@ernix - 运营商要求JavaScript,我给出了答案 是 JavaScript的。这是PHP函数的JavaScript解释。 - Kerry Jones
@ernix - 它与OP给出的示例完全一样。我放了一个小提琴,你可以看到。 - Kerry Jones
@ernix - 好的,但重点是确实如此 究竟 OP要求的是什么。它来自另一个站点(不是由我维护,我之前已经说过),并且在给出适当的变量时,它的工作方式完全如上所述。如果您认为这是一个错误,请联系phpjs.org或查看他们是否有更新版本。 - Kerry Jones


这是@ mikez302的答案的变体,但修改为支持带小数的数字(根据@ neu-rah的反馈,numberWithCommas(12345.6789) - >“12,345.6,789”而不是“12,345.6789”

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

65
2018-06-05 15:03



你看, 你是一个开源的贡献者 :) - Jacob Stamm


function formatNumber (num) {
    return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,")
}

print(formatNumber(2665));      // 2,665
print(formatNumber(102665));    // 102,665
print(formatNumber(111102665)); // 111,102,665

54
2017-08-19 06:47



我的答案不是这样做的?正则表达式看起来略有不同,但它看起来应该做同样的事情。 - Elias Zamaria
它更短:) - Tutankhamen
这很优雅。正是我在寻找什么。 - traditional
从 blog.tompawlak.org/number-currency-formatting-javascript?已知问题:formatNumber(0.123456)= 0.123,456 JS中缺少lookbehind使得使用优雅的正则表达式修复它很困难。 - Vlad
甚至与时间一起工作! 1,234:56 - sean


感谢大家的回复。我已经建立了一些答案,以制定更“一刀切”的解决方案。

第一个片段添加了一个模仿的功能 PHPnumber_format() 数字原型。如果我格式化一个数字,我通常需要小数位,所以该函数需要显示小数位数。有些国家使用逗号作为十进制和小数作为千位分隔符,因此该函数允许设置这些分隔符。

Number.prototype.numberFormat = function(decimals, dec_point, thousands_sep) {
    dec_point = typeof dec_point !== 'undefined' ? dec_point : '.';
    thousands_sep = typeof thousands_sep !== 'undefined' ? thousands_sep : ',';

    var parts = this.toFixed(decimals).split('.');
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousands_sep);

    return parts.join(dec_point);
}

您可以使用如下:

var foo = 5000;
console.log(foo.numberFormat(2)); // us format: 5,000.00
console.log(foo.numberFormat(2, ',', '.')); // european format: 5.000,00

我发现我经常需要为数学运算得到数字,但是parseFloat将5,000转换为5,只需取第一个整数值序列。所以我创建了自己的浮点转换函数并将其添加到String原型中。

String.prototype.getFloat = function(dec_point, thousands_sep) {
    dec_point = typeof dec_point !== 'undefined' ? dec_point : '.';
    thousands_sep = typeof thousands_sep !== 'undefined' ? thousands_sep : ',';

    var parts = this.split(dec_point);
    var re = new RegExp("[" + thousands_sep + "]");
    parts[0] = parts[0].replace(re, '');

    return parseFloat(parts.join(dec_point));
}

现在您可以使用以下两种功能:

var foo = 5000;
var fooString = foo.numberFormat(2); // The string 5,000.00
var fooFloat = fooString.getFloat(); // The number 5000;

console.log((fooString.getFloat() + 1).numberFormat(2)); // The string 5,001.00

31
2017-09-18 01:50



非常好,我借用了第一种方法;)但是当你想使用欧洲格式并且数字是小数时,它不会产生正确的结果。第5行应该是: var parts = this.toFixed(decimals).toString().split('.'); - vbwx
你是对的! toFixed()将逗号更改为句点,因此'。'应该用来代替 var dec_point。感谢您指出了这一点。 - J.Money
你可以为此制作一个npm模块吗? - chovy
@ J.Money .toString是不必要的,toFixed已经返回一个字符串。 - Ariel
我不知道你为什么在这里提到PHP,或者已经提供了一个原型功能 存在 - vsync


Intl.NumberFormat

原生JS功能。由IE11,Edge,最新的Safari,Chrome,Firefox,Opera,iOS上的Safari和Android上的Chrome提供支持。

var number = 3500;

console.log(new Intl.NumberFormat().format(number));
// → '3,500' if in US English locale

28
2017-10-31 23:56



并非所有浏览器都支持: caniuse.com/#feat=internationalization - Vincent Decaux
我的回答是根据您的意见更新的。谢谢! - lonelyloner