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

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个回答

1
let formatNumber = (number) => {
    let str = String(number)

    return str.split('').reduce(
        (a, b, i) => a + (i && !((str.length - i) % 3) ? ',' : '') + b,
        ''
    )
}

1
这是我见过的最简单的解决方案,没有正则表达式。 - doctorgu
@doctorgu 这很简洁,但不是最优的性能。 感谢您为我提出这个问题,我花了更多时间来尝试它,并发现了Node、Deno和Bun之间甚至有性能差异。 在这里: https://dev59.com/P3E85IYBdhLWcg3wMAfc#73861853 - Maor Ben

1
我找到了一种适用于任何情况的方法。CodeSandbox示例
function commas(n) {
  if (n < 1000) {
    return n + ''
  } else {
    // Convert to string.
    n += ''

    // Skip scientific notation.
    if (n.indexOf('e') !== -1) {
      return n
    }

    // Support fractions.
    let i = n.indexOf('.')
    let f = i == -1 ? '' : n.slice(i)
    if (f) n = n.slice(0, i)

    // Add commas.
    i = n.length
    n = n.split('')
    while (i > 3) n.splice((i -= 3), 0, ',')
    return n.join('') + f
  }
}

这类似于Noah Freitas' answer,但支持分数科学计数法
如果不考虑性能问题,我认为toLocaleString是最好的选择。 编辑:这里有一些示例的CodeSandbox:https://codesandbox.io/s/zmvxjpj6x

我尝试了 commas(12345.67) 并得到了 "12,345.6700000000000728"。 - Elias Zamaria
@EliasZamaria 哎呀!忘记了不精确的算术。已经修复 :) - aleclarson

0
这是一个支持整数和小数的一行函数。我也留了一些代码来将数字转换为字符串。
    function numberWithCommas(x) {
        return (x=x+'').replace(new RegExp('\\B(?=(\\d{3})+'+(~x.indexOf('.')?'\\.':'$')+')','g'),',');
    }

0

另一种方法是使用复杂度为n(Math.floor(str.length / 3))而不是n(str.length)

function formatNumber(num) {
  let str = String(num)
  
  if (num < 1000) return str

  let result = ''

  for (let i = Math.floor(str.length / 3); i >= 0; i--) {
    let start = str.length - 3 * (i + 1)
    let end = start + 3

    result +=
      (end ? str.slice(Math.max(0, start), end) : '') + (end && i ? ',' : '')
  }

  return result
}

我不清楚这种方法相比其他答案有什么优势。另外,我运行了 formatNumber(6543.21),得到了 6,543,.21,这个额外的逗号看起来有点奇怪。 - Elias Zamaria
@EliasZamaria 我没有涉及小数部分。你可以通过减少迭代次数到/3来实现,而不是逐个字符地进行。 - Maor Ben
Less loop虽然有效,但这个版本使用的forreduce不够有效,并且在每个循环中使用了slicemax函数。我没有比较过这个版本和旧版本,但我认为你的旧版本在现实世界中更有效,因为我们使用的大多数数字都小于一百万。 - doctorgu
我基于你的想法添加了另一种解决方案,每一步都增加3。谢谢。https://dev59.com/P3E85IYBdhLWcg3wMAfc#73883985 - doctorgu

0

又一个..(针对问题所问的整数)

function insertCommas(str)
{
    var a = str.split("");
    a.reverse();

    var t, i = 0, arr = Array();

    while (t = a.shift())
    {
       if (((i++ % 3) == 0) && arr.length > 0)
           arr.unshift(",");
       arr.unshift(t);
    }

    return arr.join("");
}

0

0
你可以在 Number 原型上创建一个函数。
Number.prototype.format = function (s, d) {
  return (
    this.toString()
      .split(".")
      .map((n, i) =>
        i
          ? n
          : n
              .split("")
              .map((n, i) => (i % 3 || !i ? n : s + n))
              .join("")
      )
      .join(d)
  );
};

console.log((8800.00).format(',', '.'))
// 8,880.00

// French notation
console.log((8800.00).format(' ', ','))
// 8 880,00

0

这是我的尝试:

编辑:添加了小数

function splitMille(n, separator = ',') {
  // Cast to string
  let num = (n + '')

  // Test for and get any decimals (the later operations won't support them)
  let decimals = ''
  if (/\./.test(num)) {
    // This regex grabs the decimal point as well as the decimal numbers
    decimals = num.replace(/^.*(\..*)$/, '$1')
  }
  
  // Remove decimals from the number string
  num = num.replace(decimals, '')
    // Reverse the number string through Array functions
    .split('').reverse().join('')
    // Split into groups of 1-3 characters (with optional supported character "-" for negative numbers)
    .match(/[0-9]{1,3}-?/g)
    // Add in the mille separator character and reverse back
    .join(separator).split('').reverse().join('')

  // Put the decimals back and output the formatted number
  return `${num}${decimals}`
}

let testA = splitMille(1234)
let testB = splitMille(-1234)
let testC = splitMille(123456.789)
let testD = splitMille(9007199254740991)
let testE = splitMille(1000.0001)

console.log('Results!\n\tA: %s\n\tB: %s\n\tC: %s\n\tD: %s\n\tE: %s', testA, testB, testC, testD, testE)


0
我来提出另一个解决方案。
首先是这个代码。
function formatNumber(number) {
  if (number < 1000) {
    return String(number);
  }
  if (number < 1000000) {
    let numbers = String(number).split('');
    numbers.splice(-3, 0, ',');
    return numbers.join('');
  }
  if (number < 1000000000) {
    let numbers = String(number).split('');
    numbers.splice(-3, 0, ',');
    numbers.splice(-7, 0, ',');
    return numbers.join('');
  }
  if (number < 1000000000000) {
    let numbers = String(number).split('');
    numbers.splice(-3, 0, ',');
    numbers.splice(-7, 0, ',');
    numbers.splice(-11, 0, ',');
    return numbers.join('');
  }

  throw new Error(`number: ${number} is too big`);
}

这段代码很冗长且不可扩展,但它很有效,因为它检查的是数字本身,而不是从数字转换而来的字符串的长度。

所以我基于这个想法转换成了简洁的代码。 (您可以将12变得更大以防止错误。)

function formatNumber(number) {
  let commas = -1;
  for (let n3 = 3; n3 <= 12; n3 += 3) {
    commas++;
    const max = Math.pow(10, n3);
    if (number < max) {
      let numbers = String(number).split('');
      for (let i = 0; i < commas; i++) {
        numbers.splice(-(3 * (i + 1) + i), 0, ',');
      }
      return numbers.join('');
    }
  }

  throw new Error(`number: ${number} is too big`);
}

0
我想分享一个小技巧,用于大数字格式化。与其插入逗号或空格,我在“千位”之间插入一个空但可见的span。这使得千位易于识别,但它允许复制/粘贴原始格式的输入,而不需要逗号/空格。
// This function accepts an integer, and produces a piece of HTML that shows it nicely with 
// some empty space at "thousand" markers. 
// Note, these space are not spaces, if you copy paste, they will not be visible.
function valPrettyPrint(orgVal) {
  // Save after-comma text, if present
  var period = orgVal.indexOf(".");
  var frac = period >= 0 ? orgVal.substr(period) : "";
  // Work on input as an integer
  var val = "" + Math.trunc(orgVal);
  var res = "";
  while (val.length > 0) {
    res = val.substr(Math.max(0, val.length - 3), 3) + res;
    val = val.substr(0, val.length - 3);
    if (val.length > 0) {
        res = "<span class='thousandsSeparator'></span>" + res;
    }
  }
  // Add the saved after-period information
  res += frac;
  return res;
}

使用这个CSS:

.thousandsSeparator {
  display : inline;
  padding-left : 4px;
}

看一个例子 JSFiddle。


你可以使用css,在保留原始格式的输入的同时显示逗号:.thousandsSeparator:before{ content: ','; }JSFiddle: https://jsfiddle.net/Dandalf/ze6agw7v/ - Dandalf
哦,等等。它只是在前面添加了一个逗号。您仍然使用我示例中的JavaScript进行呈现。所以我不确定您的意思。 - Marcin Zukowski
我进一步展示了不可选择的逗号:https://jsfiddle.net/ovfd83py/13/ - James Hulse

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