在JavaScript中,是否有一种方法可以通过Intl.NumberFormat反转格式化?

70

Intl.NumberFormat(请参见Mozilla文档)为JavaScript提供了一种很好的方式,可以将数字格式化为当前区域设置的版本,例如:

new Intl.NumberFormat().format(3400); // returns "3.400" for German locale

但我找不到逆转这种格式的方法。 有类似的东西吗?

new Intl.NumberFormat().unformat("3.400"); // returns 3400 for German locale

感谢任何帮助。


1
这个回答解决了您的问题吗?[是否有JavaScript标准API可根据区域设置解析数字?] (https://dev59.com/ZFMI5IYBdhLWcg3wxuhI) - wladimirguerra
10个回答

50

我已经找到了一个解决方法:

/**
 * Parse a localized number to a float.
 * @param {string} stringNumber - the localized number
 * @param {string} locale - [optional] the locale that the number is represented in. Omit this parameter to use the current locale.
 */
function parseLocaleNumber(stringNumber, locale) {
    var thousandSeparator = Intl.NumberFormat(locale).format(11111).replace(/\p{Number}/gu, '');
    var decimalSeparator = Intl.NumberFormat(locale).format(1.1).replace(/\p{Number}/gu, '');

    return parseFloat(stringNumber
        .replace(new RegExp('\\' + thousandSeparator, 'g'), '')
        .replace(new RegExp('\\' + decimalSeparator), '.')
    );
}

这样使用:

parseLocaleNumber('3.400,5', 'de');
parseLocaleNumber('3.400,5'); // or if you have German locale settings
// results in: 3400.5

不是最好的解决方案,但它可以工作 :-)

如果有人知道更好的方法,请随时发布您的答案。

更新

  • 包装在完整的可重用函数中
  • 使用正则表达式类\p{Number}提取分隔符。这样它也适用于非阿拉伯数字。
  • 使用具有5位数的数字来支持每四个数字分隔的语言。

您可以在返回值之前添加 + 符号将其转换为数字。当我这样做时,通常也想将其转换为数值。 - Alwin Kesler
5
喜欢这个函数!不过请注意,使用正则表达式查找千位分隔符和小数点并非必要。Intl API 还提供了 formatToParts() 函数,可以轻松提取分隔符号:const thousandSeparator = Intl.NumberFormat(i18n.language).formatToParts(11111)[1].value const decimalSeparator = Intl.NumberFormat(i18n.language).formatToParts(1.1)[1].value - Marnix.hoh
解析“123abc”的值会得到结果123,而不是NaN。 - Iúri dos Anjos
它不能处理像“$ 2,384.21”这样的货币值。它会返回“NaN”。 - wladimirguerra
1
正则表达式类\p{Number}来自哪里?我找不到任何有关它的文档。算了,我找到了:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Unicode_Property_Escapes - clapas
这对于千位分隔符为空格的情况不起作用。我必须特殊处理这种情况,然后它似乎可以工作。 - chuckles

17

这里我创建了一个反转format()函数的函数。 此函数将支持所有语言环境中的反向格式化。

function reverseFormatNumber(val,locale){
        var group = new Intl.NumberFormat(locale).format(1111).replace(/1/g, '');
        var decimal = new Intl.NumberFormat(locale).format(1.1).replace(/1/g, '');
        var reversedVal = val.replace(new RegExp('\\' + group, 'g'), '');
        reversedVal = reversedVal.replace(new RegExp('\\' + decimal, 'g'), '.');
        return Number.isNaN(reversedVal)?0:reversedVal;
    }

console.log(reverseFormatNumber('1,234.56','en'));
console.log(reverseFormatNumber('1.234,56','de'));


3
这是正确的想法,但是一旦我们开始使用其他地区设置和格式规则,就会遇到问题。有一个名为formatToParts的函数,可以用来提取组件而不是格式化。然后,您可以从formatToParts函数返回的数组中找到正确的分隔符和小数点值。 - John Leidegren
1
@JohnLeidegren 这个看起来怎么样?https://gist.github.com/OliverJAsh/8eb1d4eb3ed455f86cc8756be499ba8e - Oliver Joseph Ash
@OliverJosephAsh 是的,看起来差不多。只要记住并非所有部分都会一直存在即可。只有千位数才会有千位分隔符,只有小数才会有小数点。 - John Leidegren

4

我刚刚使用组替换解决了它

const exp = /^\w{0,3}\W?\s?(\d+)[.,](\d+)?,?(\d+)?$/g
const replacer = (f, group1, group2, group3) => {
return group3 ? 
            `${group1}${group2}.${group3}` : 
            `${group1}.${group2}`
}


const usd = '$10.15'.replace(exp, replacer)
// 10.15

const eu = '€01.25'.replace(exp, replacer)
// 1.25

const brl = 'R$ 14.000,32'.replace(exp, replacer)
// 14000.32

const tai = 'TAI 50.230,32'.replace(exp, replacer)
// 50230.32


// just to test!
const el = document.getElementById('output')

const reverseUSD = new Intl.NumberFormat('en-us', { style: 'currency', currency: 'USD' }).format(usd)

el.innerHTML += `<br> from: ${reverseUSD} to ${parseFloat(usd)}`

const reverseBRL = new Intl.NumberFormat('pt-br', { style: 'currency', currency: 'BRL' }).format(brl)

el.innerHTML += `<br> from: ${reverseBRL} to ${parseFloat(brl)}`

const reverseTAI = new Intl.NumberFormat('en-us', { style: 'currency', currency: 'TAI' }).format(tai)

el.innerHTML += `<br> from: ${reverseTAI} to ${parseFloat(tai)}`

const reverseEU = new Intl.NumberFormat('eur', { style: 'currency', currency: 'EUR' }).format(eu)

el.innerHTML += `<br> from: ${reverseEU} to ${parseFloat(eu)}`
<output id=output></output>


2
好的答案,但它有一个问题。没有小数点的数字转换将导致错误的结果。例如:R$ 2.393 将会得到 0.393 的结果。 - wladimirguerra

3
您可以使用实例上可用的formatToParts方法,这通常更好,因为您可以忽略货币和分组分隔符。

  function convertToFloat(number, locale = 'en', currency = 'AUD') {
  const instance = new Intl.NumberFormat(locale, {
    style: 'currency',
    currency,
  });
  const roundedValue = instance.formatToParts(number)
    .filter(part => !['currency', 'group'].includes(part.type))
    .reduce((acc, part) => `${acc}${part.value}`, '').replace(/,/g, '.');
  // then just parse it as a float
  return [instance.format(number), '->', parseFloat(roundedValue)];
}
console.log(convertToFloat(1234.56)); // -> 1234.56
console.log(convertToFloat(1234.56, 'de-DE', 'EUR')); // -> 1234.56


2
所做的事情是一个多步骤的过程,你可以在以下代码中看到。 nf 是 NumberFormat 服务。该函数需要格式化的数字以及使用的区域设置。现在我们通过将10k除以3来创建一个比较器,从而保证十进制和千位分隔符处于固定位置。然后删除千位分隔符和所有其他非数值符号,如货币符号。之后,我们将小数点分隔符替换为英文分隔符,最后返回一个转换后的数字。
  uf(number, locale) {
    let nf = this.nf({}, locale);
    let comparer = nf.format(10000 / 3);

    let thousandSeparator = comparer[1];
    let decimalSeparator = comparer[5];

    // remove thousand seperator
    let result = number.replace(thousandSeparator, '')
    // remove non-numeric signs except -> , .
      .replace(/[^\d.,-]/g, '')
    // replace original decimalSeparator with english one
      .replace(decimalSeparator, '.');

    // return real number
    return Number(result);
  }

有一个小问题:你必须替换所有的分隔符,因此你需要使用 let thousandSeparator = new RegExp('\\' + comparer[1], 'g'); - k102

0

我已经尝试了被接受的答案,并同意那个答案。所以我也点了赞。我可能有另一个解决方法,而不需要指定任何本地,而是手动查找分隔符。

附:数字参数用于指定小数位数。

parseLocaleNumber(stringnum: string, digit: number): number {
    let retValue: number = parseFloat(stringnum);
    var arr: string[] = stringnum.split('');
    arr.slice().reverse().forEach((x, i, arr) => {
        if (i === digit) {
            if (x === '.') {
                retValue = parseFloat(stringnum.split(',').join(''));
                arr.length = i + 1;
            } else if (x === ',') {
                retValue = parseFloat(stringnum.split('.').join(''));
                arr.length = i + 1;
            }
        }
    });
    return retValue;
}

使用此方法的示例:

console.log(parseLocaleNumber('123,456,789.12'));
// 123456789.12

这段代码是使用TypeScript编写的。


0

不确定这种方法在性能方面的相关性,但拥有几个选项总是好的,所以这里是另一种选择:

function getNumPrice(price, decimalpoint) {
    var p = price.split(decimalpoint);
    for (var i=0;i<p.length;i++) p[i] = p[i].replace(/\D/g,'');
    return p.join('.');
}

在我的情况下,区域设置是通过 PHP 设置的,因此我使用 <?php echo cms_function_to_get_decimal_point(); ?> 来获取它,但显然也可以使用其他答案中建议的除法技巧来获取。

-1

也许是一个虚拟的解决方案

const getInformattedMoney = (formattedValue) => Number(formmattedValue.replaceAll('.','').replaceAll(',','.').replace(`${currencySymbol}`,''))

对于我来说, . (点)是千位分隔符,,(逗号)是小数分隔符。

这样做可以得到更高的数字,因为小数将成为自然数的一部分。 - Felipe Candal Campos

-1

虽然不是很干净,但对我来说可行:

//value is "in-En" format --> $1,200.51
function unformatter(value){
    value = value.replace("$","");
    value = value.replace(",","");
    value = parseFloat(value);
    return(value); //returns --> 1200.51
};

€1.499,99 会产生 NaN,即使添加 并将其删除,也会产生 1.49999 而不是正确的 1499.99,因此这种方法行不通。没有一种通用的方法可以解决这个问题,除非知道输入语言环境的小数分隔符。 - CherryDT

-2

你应该可以直接使用:

value.replace(/\D/g, '');

由于任何添加的格式都将是非数字,因此需要注意。


1
不,这对于十进制数是行不通的:"10.05".replace(/\D/g, '') => "1005" - stollr
1
啊,我刚才在想整数而不是浮点数。你说得对,如果需要小数的话,这样做就行不通了。 - Ben
这实际上是整数的一种优雅解决方案。 - Yvon

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