如何将数字格式化为货币字符串

IT技术 javascript formatting currency
2021-03-11 09:13:54

我想用 JavaScript 格式化价格。我想要一个将 afloat作为参数并返回string格式如下的函数

"$ 2,500.00"

做到这一点的最佳方法是什么?

6个回答

国际数字格式

JavaScript 有一个数字格式化程序(国际化 API 的一部分)。

// Create our number formatter.
var formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',

  // These options are needed to round to whole numbers if that's what you want.
  //minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1)
  //maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501)
});

formatter.format(2500); /* $2,500.00 */

使用undefined在适当位置的第一个参数(的'en-US'中的例子)使用系统区域(在情况下,代码在浏览器中运行的用户环境)。语言环境代码的进一步解释

这是货币代码列表

Intl.NumberFormat 与 Number.prototype.toLocaleString

最后一点,将其与较旧的 . toLocaleString. 它们都提供基本相同的功能。然而, toLocaleString 在其较旧的化身(pre-Intl)中实际上并不支持语言环境:它使用系统语言环境。因此,在调试旧浏览器时,请确保您使用的是正确版本(MDN 建议检查是否存在Intl)。如果您不关心旧浏览器或仅使用shim ,则完全无需担心这一点

此外,对于单个项目,两者的性能相同,但如果您要格式化大量数字,则使用Intl.NumberFormat速度会快 70 倍。因此,通常最好在Intl.NumberFormat每次页面加载时仅使用和实例化一次。无论如何,这是等效的用法toLocaleString

(2500).toLocaleString('en-US', {
  style: 'currency',
  currency: 'USD',
}); /* $2,500.00 */

关于浏览器支持和 Node.js 的一些注意事项

  • 如今,浏览器支持不再是问题,全球支持率达 98%,美国支持率达 99%,欧盟支持率达 99% 以上
  • 有一个垫片,以支持它的化石的浏览器(如Internet Explorer 8中),你真的应该需要
  • v13 之前的 Node.js 仅支持en-US开箱即用。一种解决方案是安装full-icu,请参阅此处了解更多信息
  • 查看CanIUse了解更多信息
现在是 2018 年,基本上到处都支持。这应该是正确答案。
2021-04-23 09:13:54
最早支持 Internet Explorer 11 (IE 11),也支持所有主要浏览器。
2021-04-27 09:13:54
这是一个很好的答案,我让它使用动态货币value,所以如果在欧洲使用,那么它会更改为 EUR 并显示欧元符号。很好用!
2021-05-09 09:13:54
投票这个是因为它是一个愚蠢的简单答案,可以在本地工作。
2021-05-16 09:13:54
可以肯定的是,现在有相当多的浏览器支持这一点。这应该得到更多的支持。
2021-05-19 09:13:54

Number.prototype.toFixed

此解决方案与每个主要浏览器兼容:

  const profits = 2489.8237;

  profits.toFixed(3) // Returns 2489.824 (rounds up)
  profits.toFixed(2) // Returns 2489.82
  profits.toFixed(7) // Returns 2489.8237000 (pads the decimals)

您只需要添加货币符号(例如"$" + profits.toFixed(2)),您的金额就会以美元为单位。

自定义功能

如果您需要,在每个数字之间使用,则可以使用此功能:

function formatMoney(number, decPlaces, decSep, thouSep) {
    decPlaces = isNaN(decPlaces = Math.abs(decPlaces)) ? 2 : decPlaces,
    decSep = typeof decSep === "undefined" ? "." : decSep;
    thouSep = typeof thouSep === "undefined" ? "," : thouSep;
    var sign = number < 0 ? "-" : "";
    var i = String(parseInt(number = Math.abs(Number(number) || 0).toFixed(decPlaces)));
    var j = (j = i.length) > 3 ? j % 3 : 0;

    return sign +
        (j ? i.substr(0, j) + thouSep : "") +
        i.substr(j).replace(/(\decSep{3})(?=\decSep)/g, "$1" + thouSep) +
        (decPlaces ? decSep + Math.abs(number - i).toFixed(decPlaces).slice(2) : "");
}

document.getElementById("b").addEventListener("click", event => {
  document.getElementById("x").innerText = "Result was: " + formatMoney(document.getElementById("d").value);
});
<label>Insert your amount: <input id="d" type="text" placeholder="Cash amount" /></label>
<br />
<button id="b">Get Output</button>
<p id="x">(press button to get output)</p>

像这样使用它:

(123456789.12345).formatMoney(2, ".", ",");

如果您总是要使用 '.' 和 ',' ,您可以将它们从您的方法调用中删除,该方法将为您默认它们。

(123456789.12345).formatMoney(2);

如果您的文化翻转了两个符号(即欧洲人)并且您想使用默认值,只需在formatMoney方法中粘贴以下两行

    d = d == undefined ? "," : d,
    t = t == undefined ? "." : t,

自定义函数 (ES6)

如果您可以使用现代 ECMAScript 语法(即通过 Babel),您可以使用这个更简单的函数:

function formatMoney(amount, decimalCount = 2, decimal = ".", thousands = ",") {
  try {
    decimalCount = Math.abs(decimalCount);
    decimalCount = isNaN(decimalCount) ? 2 : decimalCount;

    const negativeSign = amount < 0 ? "-" : "";

    let i = parseInt(amount = Math.abs(Number(amount) || 0).toFixed(decimalCount)).toString();
    let j = (i.length > 3) ? i.length % 3 : 0;

    return
      negativeSign +
      (j ? i.substr(0, j) + thousands : '') +
      i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousands) +
      (decimalCount ? decimal + Math.abs(amount - i).toFixed(decimalCount).slice(2) : "");
  } catch (e) {
    console.log(e)
  }
};

document.getElementById("b").addEventListener("click", event => {
  document.getElementById("x").innerText = "Result was: " + formatMoney(document.getElementById("d").value);
});
<label>Insert your amount: <input id="d" type="text" placeholder="Cash amount" /></label>
<br />
<button id="b">Get Output</button>
<p id="x">(press button to get output)</p>

“诗歌”?更像是默默无闻。这不是代码高尔夫;使用一点空白。正确的 var 名称也不会受到伤害。
2021-04-28 09:13:54
这个 formatMoney 函数是从某个地方的一些缩小的 JavaScript 代码复制的吗?不能发原帖吗?变量 c、d、i、j、n、s 和 t 代表什么?从这篇文章的点赞和评论数量来看,我可以假设这段代码已被复制粘贴到生产网站的任何地方......如果有一天它有错误,祝你维护代码好运!
2021-04-30 09:13:54
不知道为什么人们认为这段代码很漂亮。这是无法辨认的。它似乎工作得很好,但它并不漂亮。
2021-05-14 09:13:54
首先,优秀、简洁的代码。但是,如果您是美国人,则应该分别的默认值更改为dt这样您就不必每次都指定它们。另外,我建议将语句的开头修改为,否则您将不会得到美元符号。.,returnreturn s + '$' + [rest]
2021-05-17 09:13:54

简短而快速的解决方案(适用于任何地方!)

(12345.67).toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,');  // 12,345.67

此解决方案背后的想法是用第一个匹配项和逗号替换匹配的部分,即'$&,'. 匹配是使用先行方法完成的您可以将表达式读作“匹配一个数字,如果它后跟三个数字集(一个或多个)和一个点的序列”

测试:

1        --> "1.00"
12       --> "12.00"
123      --> "123.00"
1234     --> "1,234.00"
12345    --> "12,345.00"
123456   --> "123,456.00"
1234567  --> "1,234,567.00"
12345.67 --> "12,345.67"

演示: http : //jsfiddle.net/hAfMM/9571/


扩展短解

您还可以扩展Numberobject的原型以添加对任意数量的小数[0 .. n]和数字组大小的额外支持[0 .. x]

/**
 * Number.prototype.format(n, x)
 * 
 * @param integer n: length of decimal
 * @param integer x: length of sections
 */
Number.prototype.format = function(n, x) {
    var re = '\\d(?=(\\d{' + (x || 3) + '})+' + (n > 0 ? '\\.' : '$') + ')';
    return this.toFixed(Math.max(0, ~~n)).replace(new RegExp(re, 'g'), '$&,');
};

1234..format();           // "1,234"
12345..format(2);         // "12,345.00"
123456.7.format(3, 2);    // "12,34,56.700"
123456.789.format(2, 4);  // "12,3456.79"

演示/测试: http : //jsfiddle.net/hAfMM/435/


超长短解

在这个超级扩展版本中,您可以设置不同的分隔符类型:

/**
 * Number.prototype.format(n, x, s, c)
 * 
 * @param integer n: length of decimal
 * @param integer x: length of whole part
 * @param mixed   s: sections delimiter
 * @param mixed   c: decimal delimiter
 */
Number.prototype.format = function(n, x, s, c) {
    var re = '\\d(?=(\\d{' + (x || 3) + '})+' + (n > 0 ? '\\D' : '$') + ')',
        num = this.toFixed(Math.max(0, ~~n));

    return (c ? num.replace('.', c) : num).replace(new RegExp(re, 'g'), '$&' + (s || ','));
};

12345678.9.format(2, 3, '.', ',');  // "12.345.678,90"
123456.789.format(4, 4, ' ', ':');  // "12 3456:7890"
12345678.9.format(0, 3, '-');       // "12-345-679"

演示/测试: http : //jsfiddle.net/hAfMM/612/

@Abbas 是的,替换\.$(行尾),即this.toFixed(0).replace(/(\d)(?=(\d{3})+$)/g, "$1,")
2021-04-22 09:13:54
我实际上更进一步:.replace(/(\d)(?=(\d{3})+(?:\.\d+)?$)/g, "$1,")
2021-04-29 09:13:54
@JuliendePrabère 请举一个不适用于这种方法的长数字的例子。
2021-04-30 09:13:54
@hanumant 这里的正则语法有点复杂,所以我建议你先阅读关于正则表达式的手册(例如在MDN)。它背后的想法是用第一个匹配和逗号替换匹配的部分,即$1,. 匹配是使用先行方法完成的您可以将表达式读作“匹配一个数字,如果它后跟三个数字集(一个或多个)和一个点的序列”
2021-05-04 09:13:54
带有 VisioN 和 kalisjoshua regexp 的 CoffeeScript 版本以及指定小数位的方式(因此您可以保留默认值 2 或指定 0 表示没有小数点): Number.prototype.toMoney = (decimal=2) -> @toFixed(decimal).replace /(\d)(?=(\d{3})+(?:\.\d+)?$)/g, "$1,"
2021-05-13 09:13:54

查看 JavaScript Number对象,看看它是否可以帮助您。

  • toLocaleString() 将使用特定于位置的千位分隔符格式化数字。
  • toFixed() 将数字四舍五入到特定的小数位数。

要同时使用这些值,必须将其类型改回数字,因为它们都输出一个字符串。

例子:

Number((someNumber).toFixed(1)).toLocaleString()

编辑

可以直接使用 toLocaleString 而不必重新转换为数字:

someNumber.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});

多个号码

如果您需要频繁地以类似方式格式化数字,您可以创建一个特定的对象以供重用。比如德语(瑞士):

const money = new Intl.NumberFormat('de-CH',
  { style:'currency', currency: 'CHF' });
const percent = new Intl.NumberFormat('de-CH',
  { style:'percent', maximumFractionDigits: 1, signDisplay: "always"});

可以用作:

money.format(1234.50); // output CHF 1'234.50
percent.format(0.083);  // output +8.3%

很漂亮。

谢谢!基于这个想法,我能够制作一个足够短和简单的!(和本地化)优秀。
2021-04-27 09:13:54
不知道为什么这会得到如此高的投票,但这并不能满足 OP 的要求。例如,10000将变成"10,000"而不是"10,000.00"哪个是货币格式所需的行为。
2021-04-29 09:13:54
看起来很棒,但目前几乎没有浏览器支持
2021-05-04 09:13:54
应该注意有一个toLocaleString使用系统区域设置的旧版本,以及一个来自 ECMAScript Intl API 的新(不兼容)版本。在这里解释这个答案似乎适用于旧版本。
2021-05-15 09:13:54
其实你可以。即美元:'$'+(value + 0.001).toLocaleString().slice(0,-1)
2021-05-17 09:13:54

下面是Patrick Desjardins(别名 Daok)代码,其中添加了一些注释和一些小改动:

/*
decimal_sep: character used as decimal separator, it defaults to '.' when omitted
thousands_sep: char used as thousands separator, it defaults to ',' when omitted
*/
Number.prototype.toMoney = function(decimals, decimal_sep, thousands_sep)
{
   var n = this,
   c = isNaN(decimals) ? 2 : Math.abs(decimals), // If decimal is zero we must take it. It means the user does not want to show any decimal
   d = decimal_sep || '.', // If no decimal separator is passed, we use the dot as default decimal separator (we MUST use a decimal separator)

   /*
   According to [https://stackoverflow.com/questions/411352/how-best-to-determine-if-an-argument-is-not-sent-to-the-javascript-function]
   the fastest way to check for not defined parameter is to use typeof value === 'undefined'
   rather than doing value === undefined.
   */
   t = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep, // If you don't want to use a thousands separator you can pass empty string as thousands_sep value

   sign = (n < 0) ? '-' : '',

   // Extracting the absolute value of the integer part of the number and converting to string
   i = parseInt(n = Math.abs(n).toFixed(c)) + '',

   j = ((j = i.length) > 3) ? j % 3 : 0;
   return sign + (j ? i.substr(0, j) + t : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : '');
}

这里有一些测试:

// Some tests (do not forget parenthesis when using negative numbers and number with no decimals)
alert(123456789.67392.toMoney() + '\n' + 123456789.67392.toMoney(3) + '\n' + 123456789.67392.toMoney(0) + '\n' + (123456).toMoney() + '\n' + (123456).toMoney(0) + '\n' + 89.67392.toMoney() + '\n' + (89).toMoney());

// Some tests (do not forget parenthesis when using negative numbers and number with no decimals)
alert((-123456789.67392).toMoney() + '\n' + (-123456789.67392).toMoney(-3));

细微的变化是:

  1. Math.abs(decimals)只有在没有时才移动一点NaN

  2. decimal_sep不能再为空字符串(必须使用某种小数点分隔符

  3. 我们typeof thousands_sep === 'undefined'按照如何最好地确定参数是否未发送到 JavaScript 函数中的建议使用

  4. (+n || 0)不需要因为this是一个Number对象

JSFiddle

@sohtimsso1970:抱歉回复晚了,但你能解释一下吗?我看不出哪里可以将数字解释为八进制。parseInt被称为对数的整数部分的绝对值。INTEGER 部分不能以零开头,除非它只是一个零!parseInt(0) === 0无论是八进制或十进制。
2021-05-04 09:13:54
尝试,例如: parseInt("016") ... 返回 14,因为 parseInt 假定它是八进制编码的,当字符串以零开头时。
2021-05-07 09:13:54
您可能希望使用“10”作为 parseInt 中的基数。否则,任何以“0”开头的数字都将使用八进制编号。
2021-05-15 09:13:54
@Tracker1:我知道以 开头的数字0被认为是八进制的parseInt但是在这段代码中,作为输入(或任何其他八进制格式的值)parseInt接收是不可能的016,因为传递给的参数parseIntMath.abs函数第一次处理的所以没有办法parseInt接收一个以零开头的数字,除非它只是一个零或0.nnnn小数点在哪里)。但是00.nn字符串都将被转换parseInt为一个普通的零。
2021-05-17 09:13:54
此函数不正确: > (2030).toMoney(0, '.', ' '); <“2 03 0”
2021-05-19 09:13:54