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

2655

我想在 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;
}

console.log(numberWithCommas(1000))

有没有更简单或更优雅的方法来做到这一点?如果它也可以处理浮点数,那就太好了,但这并不是必需的。它不需要是特定于区域设置的,以决定使用句点还是逗号。


70
值得注意的是,即使在2016年,Number.prototype.toLocaleString 在Safari浏览器中仍然不起作用。它只是返回数字本身,而没有错误抛出。由于这个问题,今天我感到非常无语... #goodworkApple - aendra
toLocaleString 不一致,不应使用。例如,在 Firefox 上,它将返回 1,234,但在 IE 上,它将添加小数:1,234.00。 - Bort
截至2023年,“toLocaleString”在Safari上似乎工作正常,我在Chrome、Safari和Firefox之间看到了一致的结果。 - Andrew
51个回答

11
var formatNumber = function (number) {
  var splitNum;
  number = Math.abs(number);
  number = number.toFixed(2);
  splitNum = number.split('.');
  splitNum[0] = splitNum[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  return splitNum.join(".");
}

编辑: 该函数仅适用于正数。例如:

var number = -123123231232;
formatNumber(number)

输出: "123,123,231,232"

但是要回答上面的问题, toLocaleString() 方法只是解决了这个问题。

var number = 123123231232;
    number.toLocaleString()

输出: "123,123,231,232"

加油!


1
虽然这段代码可能回答了问题,但提供有关它如何以及/或为什么解决问题的附加上下文将改善答案的长期价值。 - Donald Duck
1
不错的脚本,但它不能处理负数。 - Ralph David Abernathy

9

我的答案是唯一一个完全用更明智的替代品取代jQuery的答案:

function $(dollarAmount)
{
    const locale = 'en-US';
    const options = { style: 'currency', currency: 'USD' };
    return Intl.NumberFormat(locale, options).format(dollarAmount);
}

这个解决方案不仅会添加逗号,而且还会在您输入像$(1000.9999)这样的金额时四舍五入到最接近的一分钱,您将获得$1,001.00。 此外,您输入的值可以安全地是数字或字符串;没有关系。

如果您处理的是货币,但不想在金额上显示一个前导美元符号,您也可以添加此函数,该函数使用先前的函数,但删除了$

function no$(dollarAmount)
{
    return $(dollarAmount).replace('$','');
}

如果您不涉及货币,并且具有不同的小数格式要求,这里有一个更为通用的函数:

function addCommas(number, minDecimalPlaces = 0, maxDecimalPlaces = Math.max(3,minDecimalPlaces))
{
    const options = {};
    options.maximumFractionDigits = maxDecimalPlaces;
    options.minimumFractionDigits = minDecimalPlaces;
    return Intl.NumberFormat('en-US',options).format(number);
}

顺便说一下,这段代码在一些旧版本的Internet Explorer中无法运行是完全故意的。我尽量让它不支持现代标准时捕捉IE。

请记住,在评论区过分赞扬是被视为离题的。相反,只需用点赞来表达对我的支持。


2
Number(n).toLocaleString() 似乎是最好的答案,但如果用户只想要数字中的逗号,您可能希望使用类似于 new Intl.NumberFormat('en-US').format(n) 的东西,而不是去掉美元符号和小数点。 - bmacnaughton
@bmacnaughton:如果你不涉及到金钱,那么这是一个好观点。然而,如果你在处理金钱,并且只是“不想要前导美元符号”,Number(1000.50).toLocaleString()会产生“1,000.5”,这将删除通常在显示货币值时保留的无关紧要的零。不过,评论很好:每个人都应该知道你所说的。 - Lonnie Best

8
这是一个简单的可重复使用的函数,它返回一个指定小数位数和可以切换是否包含逗号的字符串。
function format_number(number, num_decimals, include_comma)
{
    return number.toLocaleString('en-US', {useGrouping: include_comma, minimumFractionDigits: num_decimals, maximumFractionDigits: num_decimals});
}

使用示例:

format_number(1234.56789, 2, true); // Returns '1,234.57'
format_number(9001.42, 0, false); // Returns '9001'

如果您需要进一步自定义字符串,您可以在此处找到格式选项列表。


8
我之前写过这个,然后才看到这篇文章。没有正则表达式,代码易于理解。

$(function(){
  
  function insertCommas(s) {

    // get stuff before the dot
    var d = s.indexOf('.');
    var s2 = d === -1 ? s : s.slice(0, d);

    // insert commas every 3 digits from the right
    for (var i = s2.length - 3; i > 0; i -= 3)
      s2 = s2.slice(0, i) + ',' + s2.slice(i);

    // append fractional part
    if (d !== -1)
      s2 += s.slice(d);

    return s2;

  }
  
  
  $('#theDudeAbides').text( insertCommas('1234567.89012' ) );
  
  
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<div id="theDudeAbides"></div>


1
我在函数开头添加了s.toString(),这样它就可以接受数字而不仅仅是字符串。这是我的首选答案,因为它易读、简洁,并且没有正则表达式答案似乎存在的任何错误。 - FeFiFoFu
1
只有在传递字符串时才能正常工作...这很容易修复,但我想指出一下。 - ladieu

7

对于喜欢使用单个正则表达式的1-liners,但不想使用split()函数的人来说,这是其他答案中改进后的正则表达式版本,它处理(忽略)小数位:

    var formatted = (x+'').replace(/(\..*)$|(\d)(?=(\d{3})+(?!\d))/g, (digit, fract) => fract || digit + ',');

正则表达式首先匹配以字面量"."开头的子字符串,并将其替换为本身("fract"),然后匹配任何一个数字后跟3个数字的倍数并在其后添加","。
例如,x = 12345678.12345678将给出formatted = '12,345,678.12345678'。

6
一种支持小数、不同分隔符和负数的替代方案。
var number_format = function(number, decimal_pos, decimal_sep, thousand_sep) {
    var ts      = ( thousand_sep == null ? ',' : thousand_sep )
        , ds    = ( decimal_sep  == null ? '.' : decimal_sep )
        , dp    = ( decimal_pos  == null ? 2   : decimal_pos )

        , n     = Math.floor(Math.abs(number)).toString()

        , i     = n.length % 3 
        , f     = ((number < 0) ? '-' : '') + n.substr(0, i)
    ;

    for(;i<n.length;i+=3) {
        if(i!=0) f+=ts;
        f+=n.substr(i,3);
    }

    if(dp > 0) 
        f += ds + parseFloat(number).toFixed(dp).split('.')[1]

    return f;
}

一些由@Jignesh Sanghani提出的更正,不要忘记点赞他的评论。


非常适合我,刚刚添加了一行代码,在处理之前去除了格式。 - Dennis Heiden
3
fn.substr(0, i)替换为n.substr(0, i),并将number.toFixed(dp).split('.')[1]替换为parseFloat(number).toFixed(dp).split('.')[1]。因为直接使用会产生错误,请更新您的代码。 - Jignesh Sanghani
有缺陷。数字增长了。一个示例调用会很好! - mjs
将 ceil 改为 floor 可以解决这个问题,但不确定会出现什么其他问题。 - mjs
我刚看到了你的评论,但是我记得这个问题并不简单。在某些情况下,ceil也存在缺陷。我刚刚检查了一下,如果数字是负数,则abs应该在floor之前。 - mjs
1
试试 Math.floor(-75.1) ;) - mjs

6

这激发了我去执行“npm install numeral”命令。 - steampowered

6

未来的谷歌用户(或者不一定是谷歌用户):

以上提到的所有解决方案都非常好,然而,在像这样的情况下使用RegExp可能是非常糟糕的事情。

因此,您可以使用一些提议的选项,甚至编写一些原始但有用的东西,比如:

const strToNum = str => {

   //Find 1-3 digits followed by exactly 3 digits & a comma or end of string
   let regx = /(\d{1,3})(\d{3}(?:,|$))/;
   let currStr;

   do {
       currStr = (currStr || str.split(`.`)[0])
           .replace( regx, `$1,$2`)
   } while (currStr.match(regx)) //Stop when there's no match & null's returned

   return ( str.split(`.`)[1] ) ?
           currStr.concat(`.`, str.split(`.`)[1]) :
           currStr;

};

strToNum(`123`) // => 123
strToNum(`123456`) // => 123,456
strToNum(`-1234567.0987`) // => -1,234,567.0987

这里使用的正则表达式相当简单,循环将恰好执行足够次数来完成任务。
您可以进行更好的优化,“DRYify”代码等等。
然而,
(-1234567.0987).toLocaleString();

在大多数情况下,.toLocaleString() 方法是更好的选择。重点不在于执行速度或跨浏览器兼容性。
当您想向用户显示结果数字时,在您的网站或应用程序中使用 .toLocaleString() 方法能够让您与用户说同样的语言(无论他/她的语言是什么)。
根据ECMAScript文档,该方法于1999年推出,我认为其原因是希望互联网能够将全世界人民连接起来,因此需要一些“国际化”工具。
今天,互联网确实将我们所有人连接在一起,因此重要的是记住世界比我们想象中的要复杂得多,(几乎)我们所有人都在互联网上。
显然,考虑到人们的多样性,不可能为每个人保证完美的用户体验,因为我们讲不同的语言,看重不同的东西等等。正因如此,尝试尽可能地本地化事物就更加重要了。
因此,考虑到有一些特定的标准用于表示日期、时间、数字等,我们有一个工具可以以最终用户喜欢的格式显示这些内容,那么不使用该工具(尤其是在想向用户显示这些数据的情况下)是不合适的,甚至有点不负责任。
对我来说,在这种情况下使用正则表达式代替 .toLocaleString(),听起来有点像用 JavaScript 创建时钟应用程序,并以一种仅显示布拉格时间的方式进行硬编码(这对不住不住在布拉格生活的人来说是相当无用的),即使默认行为是
new Date();

按照最终用户的时钟返回数据。

为什么你用了const和=>来编写这个函数? - OG Sean
@OGSean 我总是这样做,因为这是最方便的声明变量和函数的方式。此外,我认为它有助于保持代码更加清晰简洁。 - Igor Bykov
这就是正确的答案,人们过于复杂化了。 toLocaleString() 就是为这个精确的用例而构建的。 - serraosays

5

我认为这个函数将解决与这个问题相关的所有问题。

function commaFormat(inputString) {
    inputString = inputString.toString();
    var decimalPart = "";
    if (inputString.indexOf('.') != -1) {
        //alert("decimal number");
        inputString = inputString.split(".");
        decimalPart = "." + inputString[1];
        inputString = inputString[0];
        //alert(inputString);
        //alert(decimalPart);

    }
    var outputString = "";
    var count = 0;
    for (var i = inputString.length - 1; i >= 0 && inputString.charAt(i) != '-'; i--) {
        //alert("inside for" + inputString.charAt(i) + "and count=" + count + " and outputString=" + outputString);
        if (count == 3) {
            outputString += ",";
            count = 0;
        }
        outputString += inputString.charAt(i);
        count++;
    }
    if (inputString.charAt(0) == '-') {
        outputString += "-";
    }
    //alert(outputString);
    //alert(outputString.split("").reverse().join(""));
    return outputString.split("").reverse().join("") + decimalPart;
}

5

我认为你的解决方案是我见过的最简洁的之一。我认为没有任何标准的JavaScript函数可以做到这样的事情,所以你可能需要自己研究。

我查看了CSS 3规范,尝试找到是否可以在CSS中实现此功能,但除非您希望每个数字都有自己的<span>,否则我认为这是不可能的。

我在Google Code上找到了一个看起来很不错的项目:flexible-js-formatting。虽然我没有使用过它,但它看起来非常灵活,并且使用JsUnit进行了单元测试。该开发人员还发布了许多关于此主题的帖子(尽管有些旧)。

请注意考虑国际用户:许多国家使用空格作为分隔符,并使用逗号将小数与整数部分分开。


你可以发布一个只使用CSS和Spans的解决方案吗? - Chris Betti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接