【问题标题】:Regex to separate thousands with comma and keep two decimals正则表达式用逗号分隔千位并保留两位小数
【发布时间】:2017-09-06 12:03:12
【问题描述】:

我最近在回答另一个 StackOverflow 问题时想出了这段代码。基本上,在模糊时,此代码将正确地以千位逗号分隔,并将小数点保留为两位数(如 USD 的书写方式 [7,745.56])。

我想知道是否有更简洁的方法来使用正则表达式来分隔和切断过多的小数位。我最近用我最近的尝试更新了这篇文章。有没有更好的方法来使用正则表达式?

输入 -> 目标输出

7456 -> 7,456
45345 -> 45,345
25.23523534 -> 25.23
3333.239 -> 3,333.23
234.99 -> 234.99
2300.99 -> 2,300.99
23123123123.22 -> 23,123,123,123.22

当前正则表达式

var result;
var str = []
reg = new RegExp(/(\d*(\d{2}\.)|\d{1,3})/, "gi");
reversed = "9515321312.2323432".split("").reverse().join("")
while (result = reg.exec(reversed)) {
  str.push(result[2] ? result[2] : result[0])
}
console.log(str.join(",").split("").reverse().join("").replace(",.","."))

【问题讨论】:

  • 我不认为正则表达式是为了添加字符。所以如果我有10000.50 你想要10,000.5000 对吗?
  • @Rajesh 不,如果我有 1000.562343,那么我想要 1000.56。但是感谢您指出我不能添加小数...没想到这一点。
  • 可能是这样的? stackoverflow.com/a/30106316/916000
  • value.toLocaleString('en-US', {maximumFractionDigits:2}) 有什么兼容性问题?它适用于 Firefox、Chrome 和 IE。
  • toLocaleString 仅用于此目的。对所有事情都滥用正则表达式并不好。你说toFixed 很乱!?这里使用的正则表达式更加混乱。还有@ConnorsFan,你为什么删除你的答案?这是this question which seems to be suffering from an XY problem的唯一正确答案。

标签: javascript jquery regex


【解决方案1】:

作为 Regex 的替代方法,您可以使用以下方法

Number(num.toFixed(2)).toLocaleString('en-US')

或

num.toLocaleString('en-US', {maximumFractionDigits: 2})

您仍然可以使用toFixed(2),但它非常干净。 toFixed(2) 虽然不会像你想要的那样降低数字。与{maximumFractionDigits: 2} 相同,作为toLocaleString 的第二个参数也是如此。

var nums = [7456, 45345, 25.23523534, 3333.239, 234.99, 2300.99, 23123123123.22]

for (var num of nums) 
  console.log(num, '->',  Number(num.toFixed(2)).toLocaleString('en-US') )

像你展示的那样设置数字有点棘手。做类似(num * 100 | 0) / 100 的事情是行不通的。计算失去精度(例如,在某些情况下,0.99 将变为 0.98)。 (|0 也不适用于更大的数字,但即使 Math.floor() 也存在精度问题)。

解决方案是将数字视为字符串。

function format(num) {
    var num = num.toLocaleString('en-US')
    var end = num.indexOf('.') < 0 ? num.length : num.indexOf('.') + 3
    return num.substring(0, end)
}

var nums = [7456, 45345, 25.23523534, 3333.239, 234.99, 2300.99, 23123123123.22]

for (var num of nums) console.log(num, '->', format(num))

function format(num) {
  var num = num.toLocaleString('en-US')
  var end = num.indexOf('.') < 0 ? num.length : num.indexOf('.') + 3
  return num.substring(0, end)
}

(当更改为'en-US' 以外的其他格式时,请注意数字中的.,因为某些语言使用, 作为分形分隔符)

对于兼容性,根据CanIUsetoLocaleString('en-US')是

在所有浏览器中都有效支持(从 IE6+、Firefox 2+、Chrome 1+等)

【讨论】:

  • 这是它需要做的方式。 toLocaleString 仅用于此目的。对所有事情都滥用正则表达式并不好。顺便说一句,您可以通过将{minimumFractionDigits: 2} 的对象参数传递给toLocaleString 来摆脱toFixed。
  • @Abhitalks 谢谢,我不知道这个功能。但是,似乎{maximumFractionDigits: 2} 更合适。任何一种解决方案都会对数字进行四舍五入,而不是简单地将它们取整。
【解决方案2】:

如果您真的坚持纯粹在正则表达式中执行此操作(并且截断而不是舍入小数位),我能想到的唯一解决方案是使用替换函数作为 .replace() 的第二个参数:

('' + num).replace(
  /(\d)(?=(?:\d{3})+(?:\.|$))|(\.\d\d?)\d*$/g, 
  function(m, s1, s2){
    return s2 || (s1 + ',');
  }
);

这会让你所有的测试用例都通过:

function format(num){
  return ('' + num).replace(
    /(\d)(?=(?:\d{3})+(?:\.|$))|(\.\d\d?)\d*$/g, 
    function(m, s1, s2){
      return s2 || (s1 + ',');
    }
  );
}


test(7456, "7,456");
test(45345, "45,345");
test(25.23523534, "25.23"); //truncated, not rounded
test(3333.239, "3,333.23"); //truncated, not rounded
test(234.99, "234.99");
test(2300.99, "2,300.99");
test(23123123123.22, "23,123,123,123.22");

function test(num, expected){
  var actual = format(num);
  console.log(num + ' -> ' + expected + ' => ' + actual + ': ' + 
    (actual === expected ? 'passed' : 'failed')
   );
}

【讨论】:

  • 如果我不想在小数之间添加逗号怎么办?例如,这个数字 1233489.4545 会变成这样:1,233,489.4545 而不是这样:1,233,489.4,545
  • @NewshaNik,此代码不会在小数之间添加逗号,它只会截断为两位小数。您输入的 1233489.4545 将输出为 1,233,489.45。
【解决方案3】:

我在正则表达式逗号添加逻辑之上添加了另一层,其中正则表达式将不需要的小数降至百分之一以下;

val.replace(/(\.\d{2})\d*/, "$1").replace(/(\d)(?=(\d{3})+\b)/g, "$1,")

doIt("7456");
doIt("45345");
doIt("25.23523534");
doIt("3333.239");
doIt("234.99");
doIt("2300.99");
doIt("23123123123.22");
doIt("5812090285.2817481974897");

function doIt(val) {
    console.log(val + " -> " + val.replace(/(\.\d{2})\d*/, "$1").replace(/(\d)(?=(\d{3})+\b)/g, "$1,"));
}

如果多次调用 regex replace 没问题,这个答案应该会让你满意,因为它只有 regex replace 逻辑,没有别的。

【讨论】:

  • 我很确定这仅在一个正则表达式替换中是可能的。不过感谢您的想法。
  • @PavelReznikov 怎么样?它按预期工作7,456.00,如果有任何小数,它会按照 OP 的要求保留它。
【解决方案4】:

这是一种不用正则表达式的方法:

value.toLocaleString("en-US", { maximumFractionDigits: 2 })

function formatValue() {
    var source = document.getElementById("source");
    var output = document.getElementById("output");
    var value = parseFloat(source.value);
    output.innerText = value.toLocaleString("en-US", { maximumFractionDigits: 2 });
}
<input id="source" type="text" />
<button onclick="formatValue()">Format</button>
<div id="output"></div>

【讨论】:

    【解决方案5】:

    RegEx 再次拯救!

    我的解决方案有两个部分:

    .toFixed:用于限制小数限制

    /(\d)(?=(\d\d\d)+(?!\d))/g : 它使用一次三个数字的反向引用

    以下是所有内容:

    // .toFixed((/\./g.test(num)) ? 2 : 0) it tests if the input number has any decimal places, if so limits it to 2 digits and if not, get's rid of it altogether by setting it to 0
    num.toFixed((/\./g.test(num)) ? 2 : 0).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,"))
    

    你可以在这里看到它的实际效果:

    var input = [7456, 45345, 25.23523534, 3333.239, 234.99, 2300.99, 23123123123.22]
    
    input.forEach(function(num) {
      $('div')
        .append(
          $('<p>').text(num + ' => ' +
            num.toFixed( (/\./g.test(num))?2:0 ).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,"))
        );
    });
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div> </div>

    注意:我只使用 jQuery 来追加结果

    【讨论】:

      【解决方案6】:

      试试:

      var n = 5812090285.2817481974897;
      n = n.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');
      console.log(n);
      

      输出:

      5,812,090,285.28
      

      注意:.toFixed(2) 返回一个string。因此,为了进一步简化这一点,您必须在执行正则表达式之前添加一种将n 转换为string 的方法。例如:

      n.toString.replace(/(\d)(?=(\d{3})+\.)/g, '$1,');  //ofc with the additional regex
      

      虽然您会认为这在 javascript 中无关紧要,但显然在这种情况下确实如此。所以我不知道不使用会有多“少”的混乱。

      【讨论】:

      • 几乎,我正在尝试删除其中的toFixed 部分。应该有一种纯粹在 REGEX 中执行此操作的方法。 (我知道如果数字是整数,我不能通过正则表达式添加 .00,但我想通过正则表达式切掉多余的小数)。
      • 我不知道你可以进一步简化多少,但我会看看我是否能想到/找到一些东西。
      【解决方案7】:

      你可以这样做

      (parseFloat(num).toFixed(2)).replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,").replace(".00","")
      

      这里只是将数字转换为格式化数字,向下舍入到小数点后 2 位,然后删除 .00(如果存在)。

      这可能是您可以使用的一种方法。

      var format = function (num) {
          
      return (parseFloat(num).toFixed(2)).replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,").replace(".00","")
      }
      $(function () {
          $("#principalAmtOut").blur(function (e) {
              $(this).val(format($(this).val()));
          });
      });
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
      <input id="principalAmtOut" type="text" />

      【讨论】:

        【解决方案8】:

        您可以使用Intl.NumberFormat,将style 设置为"decimal",并将maximumFractionDigits 设置为2,options 对象在第二个参数处传递

        const nums = [7456, 45345, 25.23523534, 3333.239, 234.99, 2300.99, 23123123123.22];
        
        const formatOptions = {style:"decimal", maximumFractionDigits:2};
                   
        const formatter = new Intl.NumberFormat("en-US", formatOptions);
        
        const formatNums = num => formatter.format(num);
        
        let formattedNums = nums.map(formatNums);
        
        console.log(formattedNums);

        【讨论】:

          【解决方案9】:

          我根据@Pierre 的回答找到了一个没有使用toFixed 的解决方案:

          function format(n) {
            n = +n;
            var d = Math.round(n * 100) % 100;
            return (Math.floor(n) + '').replace(/(\d)(?=(\d{3})+$)/g, '$1,') + (d > 9 ? '.' + d : d > 0 ? '.0' + d : '');
          }
          
          console.log(format(7456));
          console.log(format(7456.0));
          console.log(format(7456.1));
          console.log(format(7456.01));
          console.log(format(7456.001));
          console.log(format(45345));
          console.log(format(25.23523534));
          console.log(format(3333.239));
          console.log(format(234.99));
          console.log(format(2300.99));
          console.log(format(23123123123.22));
          console.log(format('23123123123.22'));

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-08-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-10-21
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多