在JavaScript中将带点号或逗号作为小数分隔符的字符串转换为数字

73
一个输入元素包含数字a,其中逗号或句点用作小数分隔符,空格可以用来表示千位分组,例如:

'1,2'
'110 000,23'
'100 1.23'

如何使用JavaScript在浏览器中将它们转换为浮点数?
使用jQuery和jQuery UI。Number(string)返回NaN,而parseFloat()则停止在第一个空格或逗号处。

1
未来的读者请注意,下面的答案中几乎每个 replace 都应该改为 replaceAll - Sebastian Simon
12个回答

58

首先进行替换:

parseFloat(str.replace(',','.').replace(' ',''))

91
这完全忽略了逗号常被用作千分位分隔符的事实。如果你在一个像“1,000,000.00”这样的数字上使用它,你会得到“1”,显然完全错误。 - Vala
17
@Zbynek 我并不是在假设任何事情。大多数语言/地区都使用某种方式的数字分组。所用字符有所不同(例如 [ .,'])。我指出的事实是,这段代码忽略了大量可能导致答案不正确而不提供警告或无法知道其发生的输入。即使你列出的语言没有这个问题,也不能否认在很多很多语言中会存在这个问题。请注意,如果所涉及的语言使用 . 作为其数字分组字符,它也将同样失效。 - Vala
21
这是我在SO见过的最糟糕的解决方案!SO是基于社区的,忽略了一个事实:即使解决方案对提问者有效,它通常也不是正确答案。如果我在财务软件中使用这种解决方案,现在我可能已经坐牢或被杀死了。 - Subliminal Hash
4
我认为这是一个不错的答案。我讨厌用户试图利用边角情况来破坏适用于某些人的好答案。如果您的环境和客户可以保证,例如文化是 EN 或者没有财务数据会像这样被转换,那有什么大不了的呢?难道代码只有在设计过度的瑞士军刀的情况下才能被“接受”吗?这段代码真的那么差到有人因此而丧生吗,@KemalEmin? - Zimano
3
@Zimano 如果它能够明确地突出其局限性,而不是将自己呈现为一个没有警告的简单解决方案,那么这将是一个不错的答案。当它失败时,它会默默地和惊人地失败,这应该至少对那些正在寻找类似于原始问题所问的解决方案的人们发出警告。SO旨在为不仅仅是提出问题的人在他们描述的情况下提供解决方案。 - Vala
显示剩余2条评论

33

我意识到自己来晚了,但是我想要一个解决方案,它可以正确处理数字分组以及货币使用不同的小数分隔符。由于没有一个选项完全适合我的使用情况,所以我编写了自己的解决方案,这可能对其他人也有用:

function parsePotentiallyGroupedFloat(stringValue) {
    stringValue = stringValue.trim();
    var result = stringValue.replace(/[^0-9]/g, '');
    if (/[,\.]\d{2}$/.test(stringValue)) {
        result = result.replace(/(\d{2})$/, '.$1');
    }
    return parseFloat(result);
}

这应该剔除任何非数字字符,然后检查是否有小数点(或逗号)后跟着两个数字,并在需要时插入小数点。

值得注意的是,我专门针对货币进行了设计,因此它假定没有小数位或恰好有两位小数。如果不知道当前语言环境的具体细节,很难确定遇到的第一个潜在小数点是小数点还是数字分组字符(例如,1.542 可能是 1542),但是通过更改 \d{2}$ 以适当匹配期望的小数点后内容,可以很容易地将其调整为特定用例。


3
为支持负数值,在正则表达式中的数字9后添加\-(转义减号)。应该像这样:var result = stringValue.replace(/[^0-9\-]/g, ''); - Lucas Rueda
你实际上不必转义减号,但是没问题,那样也可以。 - Vala

22

谢谢。这个库在执行取消格式化时没有提供检查不当格式的选项,但它是构建该功能的良好基础库。模糊结果的示例:http://jsfiddle.net/SkzYe/ - John Zabroski
1
为了以后的参考,请注意,此库已经迁移到http://openexchangerates.github.io/accounting.js/。 - Vala

9
您可以将所有的空格替换为一个空字符串,所有的逗号替换为点,然后进行解析。
var str = "110 000,23";
var num = parseFloat(str.replace(/\s/g, "").replace(",", "."));
console.log(num);

我在第一个中使用了正则表达式,以便匹配所有空格,而不仅仅是第一个空格。

7

这是最佳解决方案

http://numeraljs.com/


(注:该内容无需翻译)
numeral().unformat('0.02'); = 0.02

6

关于:

parseFloat(str.replace(' ', '').replace('.', '').replace(',', '.'));

6

所有其他解决方案都需要您预先知道格式。在每种情况下,我需要检测(!)格式,这就是我最终得到的结果。

function detectFloat(source) {
    let float = accounting.unformat(source);
    let posComma = source.indexOf(',');
    if (posComma > -1) {
        let posDot = source.indexOf('.');
        if (posDot > -1 && posComma > posDot) {
            let germanFloat = accounting.unformat(source, ',');
            if (Math.abs(germanFloat) > Math.abs(float)) {
                float = germanFloat;
            }
        } else {
            // source = source.replace(/,/g, '.');
            float = accounting.unformat(source, ',');
        }
    }
    return float;
}

这是针对以下情况进行的测试:
        const cases = {
            "0": 0,
            "10.12": 10.12,
            "222.20": 222.20,
            "-222.20": -222.20,
            "+222,20": 222.20,
            "-222,20": -222.20,
            "-2.222,20": -2222.20,
            "-11.111,20": -11111.20,
        };

欢迎提出建议。

1
这是一个相当不错的想法,如果能够在不使用account.js的情况下实现,那将成为一个很好的独立解决方案。 - Chris

5

这是我的解决方案,没有任何依赖:

return value
  .replace(/[^\d\-.,]/g, "")   // Basic sanitization. Allows '-' for negative numbers
  .replace(/,/g, ".")          // Change all commas to periods
  .replace(/\.(?=.*\.)/g, ""); // Remove all periods except the last one

(我忽略了转换为数字的步骤-如果您不关心JavaScript浮点数的精度问题,那可能只是一个 parseFloat 调用。)

该代码假设:

  • 只有逗号和句点用作小数分隔符。(我不确定是否存在使用其他分隔符的语言环境。)
  • 字符串的小数部分不使用任何分隔符。

5

下面是一个自给自足的JS函数,它可以解决大多数欧洲/美国地区(主要是在美国/德国/瑞典数字分块和格式化方面)的问题,包括OP中提到的问题。我认为它比Slawa的解决方案更好,并且不需要任何依赖。

function realParseFloat(s)
{
    s = s.replace(/[^\d,.-]/g, ''); // strip everything except numbers, dots, commas and negative sign
    if (navigator.language.substring(0, 2) !== "de" && /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(s)) // if not in German locale and matches #,###.######
    {
        s = s.replace(/,/g, ''); // strip out commas
        return parseFloat(s); // convert to number
    }
    else if (/^-?(?:\d+|\d{1,3}(?:\.\d{3})+)(?:,\d+)?$/.test(s)) // either in German locale or not match #,###.###### and now matches #.###,########
    {
        s = s.replace(/\./g, ''); // strip out dots
        s = s.replace(/,/g, '.'); // replace comma with dot
        return parseFloat(s);
    }
    else // try #,###.###### anyway
    {
        s = s.replace(/,/g, ''); // strip out commas
        return parseFloat(s); // convert to number
    }
}

1
123.456,789(从德国的角度来看)返回的是123.456789而不是123456.789。 - Revo
@Revo 感谢您的评论。您确定吗?当我使用“123.456,789”运行此代码时,我得到的结果是 123456.789。“123,456.789”将不会匹配第一个条件的正则表达式,但它确实匹配第二个条件的正则表达式,因此应该被处理为 123456.789。 - niceblue
抱歉,我的意思是:(德语)123,456.789 变成了 123456.789,而不是预期的 123.456789;(英语)123.456,789 变成了 123456.789,而不是预期的 123.456789。 - Revo
1
@Revo,谢谢。有一件事不太清楚,就是在德语中,小数点是否也用“.”分隔,因为在其他系统中它们并没有被分隔。我在互联网上很难找到任何关于这个正式定义的信息(Windows中的Microsoft官方本地化并没有指定它被分隔)。 - niceblue
niceblue,你对Revo的小数处理提出了公正的观点。永远不要使用任何符号来分隔小数。因此,@Revo,请重新考虑你使用的格式,因为这真的不是处理长小数的方式。即使在德国也不是这样的。 - Marijke
显示剩余2条评论

1

试试这个...

var withComma = "23,3";
var withFloat = "23.3";

var compareValue = function(str){
  var fixed = parseFloat(str.replace(',','.'))
  if(fixed > 0){
      console.log(true)
    }else{
      console.log(false);
  }
}
compareValue(withComma);
compareValue(withFloat);

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