简体   繁体   English

如何将带有逗号千位分隔符的字符串解析为数字?

[英]How can I parse a string with a comma thousand separator to a number?

I have 2,299.00 as a string and I am trying to parse it to a number.我有2,299.00作为字符串,我试图将它解析为一个数字。 I tried using parseFloat , which results in 2. I guess the comma is the problem, but how would I solve this issue the right way?我尝试使用parseFloat ,结果为 2。我猜逗号是问题所在,但我该如何正确解决这个问题? Just remove the comma?只是去掉逗号?

 var x = parseFloat("2,299.00") console.log(x);

Yes remove the commas:是的,删除逗号:

 let output = parseFloat("2,299.00".replace(/,/g, '')); console.log(output);

Removing commas is potentially dangerous because, as others have mentioned in the comments, many locales use a comma to mean something different (like a decimal place).删除逗号有潜在的危险,因为正如其他人在评论中提到的那样,许多语言环境使用逗号来表示不同的东西(比如小数位)。

I don't know where you got your string from, but in some places in the world "2,299.00"<\/code> = 2.299<\/code>我不知道你从哪里得到你的字符串,但在世界上的某些地方“2,299.00 "2,299.00"<\/code> = 2.299<\/code>

The Intl<\/code> object could have been a nice way to tackle this problem, but somehow they managed to ship the spec with only a Intl.NumberFormat.format()<\/code> API and no parse<\/code> counterpart :( Intl<\/code>对象可能是解决此问题的好方法,但不知何故,他们设法仅使用Intl.NumberFormat.format()<\/code> API 交付规范,而没有parse<\/code>对应项:(

The only way to parse a string with cultural numeric characters in it to a machine recognisable number in any i18n sane way is to use a library that leverages CLDR data to cover off all possible ways of formatting number strings http:\/\/cldr.unicode.org\/<\/a>以任何 i18n 理智的方式将带有文化数字字符的字符串解析为机器可识别的数字的唯一方法是使用利用 CLDR 数据的库来覆盖格式化数字字符串的所有可能方式http:\/\/cldr.unicode。组织\/<\/a>

The two best JS options I've come across for this so far:到目前为止,我遇到的两个最佳 JS 选项:

On modern browsers you can use the built in Intl.NumberFormat to detect the browser's number formatting and normalize the input to match.在现代浏览器上,您可以使用内置的Intl.NumberFormat来检测浏览器的数字格式并将输入标准化以匹配。

 function parseNumber(value, locales = navigator.languages) { const example = Intl.NumberFormat(locales).format('1.1'); const cleanPattern = new RegExp(`[^-+0-9${ example.charAt( 1 ) }]`, 'g'); const cleaned = value.replace(cleanPattern, ''); const normalized = cleaned.replace(example.charAt(1), '.'); return parseFloat(normalized); } const corpus = { '1.123': { expected: 1.123, locale: 'en-US' }, '1,123': { expected: 1123, locale: 'en-US' }, '2.123': { expected: 2123, locale: 'fr-FR' }, '2,123': { expected: 2.123, locale: 'fr-FR' }, } for (const candidate in corpus) { const { locale, expected } = corpus[candidate]; const parsed = parseNumber(candidate, locale); console.log(`${ candidate } in ${ corpus[ candidate ].locale } == ${ expected }? ${ parsed === expected }`); }

Their's obviously room for some optimization and caching but this works reliably in all languages.他们显然有一些优化和缓存的空间,但这在所有语言中都可靠地工作。

Remove anything that isn't a digit, decimal point, or minus sign ( -<\/code> ):删除任何不是数字、小数点或减号 ( -<\/code> ) 的内容:

var str = "2,299.00";
str = str.replace(/[^\d\.\-]/g, ""); // You might also include + if you want them to be able to type it
var num = parseFloat(str);

Usually you should consider to use input fields which don't allow free text input for numeric values.通常您应该考虑使用不允许自由文本输入数值的输入字段。 But there might be cases, when you need to guess the input format.但在某些情况下,您可能需要猜测输入格式。 For example 1.234,56 in Germany means 1,234.56 in US.例如,德国的 1.234,56 表示美国的 1,234.56。 See https://salesforce.stackexchange.com/a/21404 for a list of countries which use comma as decimal.有关使用逗号作为十进制的国家/地区列表,请参阅https://salesforce.stackexchange.com/a/21404

I use the following function to do a best guess and strip off all non-numeric characters:我使用以下函数进行最佳猜测并去除所有非数字字符:

function parseNumber(strg) {
    var strg = strg || "";
    var decimal = '.';
    strg = strg.replace(/[^0-9$.,]/g, '');
    if(strg.indexOf(',') > strg.indexOf('.')) decimal = ',';
    if((strg.match(new RegExp("\\" + decimal,"g")) || []).length > 1) decimal="";
    if (decimal != "" && (strg.length - strg.indexOf(decimal) - 1 == 3) && strg.indexOf("0" + decimal)!==0) decimal = "";
    strg = strg.replace(new RegExp("[^0-9$" + decimal + "]","g"), "");
    strg = strg.replace(',', '.');
    return parseFloat(strg);
}   

Try it here: https://plnkr.co/edit/9p5Y6H?p=preview在这里试试: https ://plnkr.co/edit/9p5Y6H?p=preview

Examples:例子:

1.234,56 € => 1234.56
1,234.56USD => 1234.56
1,234,567€ => 1234567
1.234.567 => 1234567
1,234.567 => 1234.567
1.234 => 1234 // might be wrong - best guess
1,234 => 1234 // might be wrong - best guess
1.2345 => 1.2345
0,123 => 0.123

The function has one weak point: It is not possible to guess the format if you have 1,123 or 1.123 - because depending on the locale format both might be a comma or a thousands-separator.该函数有一个弱点:如果您有 1,123 或 1.123,则无法猜测格式 - 因为根据语言环境格式,两者都可能是逗号或千位分隔符。 In this special case the function will treat separator as a thousands-separator and return 1123.在这种特殊情况下,该函数会将分隔符视为千位分隔符并返回 1123。

It's baffling that they included a toLocaleString<\/strong> but not a parse<\/strong> method.令人费解的是,它们包含一个toLocaleString<\/strong>但没有一个parse<\/strong>方法。 At least toLocaleString<\/strong> without arguments is well supported in IE6+. IE6+ 至少支持不带参数的toLocaleString<\/strong> 。

For a i18n<\/strong> solution, I came up with this:对于i18n<\/strong>解决方案,我想出了这个:

First detect the user's locale decimal separator:首先检测用户的语言环境小数点分隔符:

var decimalSeparator = 1.1;
decimalSeparator = decimalSeparator.toLocaleString().substring(1, 2);

This is a simplistic unobtrusive wrapper around the parseFloat function. 这是parseFloat函数周围的一个简单的,不打扰的包装器。

function parseLocaleNumber(str) {
  // Detect the user's locale decimal separator:
  var decimalSeparator = (1.1).toLocaleString().substring(1, 2);
  // Detect the user's locale thousand separator:
  var thousandSeparator = (1000).toLocaleString().substring(1, 2);
  // In case there are locales that don't use a thousand separator
  if (thousandSeparator.match(/\d/))
    thousandSeparator = '';

  str = str
    .replace(new RegExp(thousandSeparator, 'g'), '')
    .replace(new RegExp(decimalSeparator), '.')

  return parseFloat(str);
}

If you want to avoid the problem that David Meister posted and you are sure about the number of decimal places, you can replace all dots and commas and divide by 100, ex.:如果您想避免 David Meister 发布的问题并且您确定小数位数,您可以替换所有点和逗号并除以 100,例如:

var value = "2,299.00";
var amount = parseFloat(value.replace(/"|\,|\./g, ''))/100;

or try this shorter approach:或者试试这个更短的方法:

const myNum =  +('2,299.00'.replace(",",""));

If you have several commas use Regex:如果您有多个逗号,请使用正则表达式:

const myNum =  +('2,022,233,988.55'.replace(/,/g,""));
// -> myNum = 2022233988.55

Here was my case in an array (for similar use case):这是我在数组中的案例(对于类似的用例):

To get the sum of this array:要获取此数组的总和:

const numbers = ["11", "7", "15/25", "18/5", "12", "16/25"]

By using parseFloat I would lose the decimals so to get the exact sum I had to first replace the forward slash with dot, then convert the strings to actual numbers.通过使用parseFloat我会丢失小数点,所以为了得到准确的总和,我必须首先用点替换正斜杠,然后将字符串转换为实际数字。

So:所以:

const currectNumbers = numbers.map(num => +(num.replace("/",".")))

// or the longer approach:
const currectNumbers = numbers
.map(num => num.replace("/","."))
.map(num => parseFloat(num));

This will give me the desired array to be used in reduce method:这将为我提供在 reduce 方法中使用的所需数组:

currectNumbers = [ 11, 7, 15.25, 18.5, 12, 16.25]

This converts a number in whatever locale to normal number.这会将任何语言环境中的数字转换为正常数字。 Works for decimals points too:也适用于小数点:

function numberFromLocaleString(stringValue, locale){
    var parts = Number(1111.11).toLocaleString(locale).replace(/\d+/g,'').split('');
    if (stringValue === null)
        return null;
    if (parts.length==1) {
        parts.unshift('');
    }   
    return Number(String(stringValue).replace(new RegExp(parts[0].replace(/\s/g,' '),'g'), '').replace(parts[1],"."));
}

All of these answers fail if you have a number in the millions.如果您有数百万的数字,所有这些答案都会失败。

3,456,789 would simply return 3456 with the replace method. 3,456,789 将简单地使用替换方法返回 3456。

The most correct answer for simply removing the commas would have to be.简单地删除逗号的最正确答案必须是。

var number = '3,456,789.12';
number.split(',').join('');
/* number now equips 3456789.12 */
parseFloat(number);
const parseLocaleNumber = strNum => {
    const decSep = (1.1).toLocaleString().substring(1, 2);
    const formatted = strNum
        .replace(new RegExp(`([${decSep}])(?=.*\\1)`, 'g'), '')
        .replace(new RegExp(`[^0-9${decSep}]`, 'g'), '');
    return Number(formatted.replace(decSep, '.'));
};

With this function you will be able to format values in multiple formats like 1.234,56<\/code> and 1,234.56<\/code> , and even with errors like 1.234.56<\/code> and 1,234,56<\/code>使用此功能,您将能够以多种格式格式化值,如1.234,56<\/code>和1,234.56<\/code> ,甚至出现1.234.56<\/code>和1,234,56<\/code>等错误

/**
 * @param {string} value: value to convert
 * @param {bool} coerce: force float return or NaN
 */
function parseFloatFromString(value, coerce) {
    value = String(value).trim();

    if ('' === value) {
        return value;
    }

    // check if the string can be converted to float as-is
    var parsed = parseFloat(value);
    if (String(parsed) === value) {
        return fixDecimals(parsed, 2);
    }

    // replace arabic numbers by latin
    value = value
    // arabic
    .replace(/[\u0660-\u0669]/g, function(d) {
        return d.charCodeAt(0) - 1632;
    })

    // persian
    .replace(/[\u06F0-\u06F9]/g, function(d) {
        return d.charCodeAt(0) - 1776;
    });

    // remove all non-digit characters
    var split = value.split(/[^\dE-]+/);

    if (1 === split.length) {
        // there's no decimal part
        return fixDecimals(parseFloat(value), 2);
    }

    for (var i = 0; i < split.length; i++) {
        if ('' === split[i]) {
            return coerce ? fixDecimals(parseFloat(0), 2) : NaN;
        }
    }

    // use the last part as decimal
    var decimal = split.pop();

    // reconstruct the number using dot as decimal separator
    return fixDecimals(parseFloat(split.join('') +  '.' + decimal), 2);
}

function fixDecimals(num, precision) {
    return (Math.floor(num * 100) / 100).toFixed(precision);
}
Number("2,299.00".split(',').join(''));   // 2299

If you want a l10n answer do it this way.如果您想要一个 l10n 答案,请这样做。 Example uses currency, but you don't need that.示例使用货币,但您不需要它。 Intl library will need to be polyfilled if you have to support older browsers.如果您必须支持旧版浏览器,则需要对 Intl 库进行填充。

var value = "2,299.00";
var currencyId = "USD";
var nf = new Intl.NumberFormat(undefined, {style:'currency', currency: currencyId, minimumFractionDigits: 2});

value = nf.format(value.replace(/,/g, ""));

If you have a small set of locales to support you'd probably be better off by just hardcoding a couple of simple rules:如果你有一小部分语言环境来支持你可能会更好地通过硬编码几个简单的规则:

function parseNumber(str, locale) {
  let radix = ',';
  if (locale.match(/(en|th)([-_].+)?/)) {
    radix = '.';
  }
  return Number(str
    .replace(new RegExp('[^\\d\\' + radix + ']', 'g'), '')
    .replace(radix, '.'));
}

Based on many great architects here, I've simplified it a bit.基于这里许多伟大的架构师,我对其进行了一些简化。

I prefer to use Intl.NumberFormat(undefined) to make it use the best fit mechanism.我更喜欢使用Intl.NumberFormat(undefined)来使其使用best fit的机制。

If the user, like me, has a Danish keyboard, but prefer the Mac to be english, this helps: if (Number.isNaN(normalized)) return Number(value.replace(',', '.'));如果用户像我一样使用丹麦语键盘,但更喜欢 Mac 为英语,这会有所帮助: if (Number.isNaN(normalized)) return Number(value.replace(',', '.'));

If this is used in a form, I found that I should use inputMode="numeric" rather than type="number" .如果在表单中使用它,我发现我应该使用inputMode="numeric"而不是type="number"

 function parseNumber(value, locales = undefined) { if (typeof value;== 'string') return value. const example = Intl.NumberFormat(locales).format('1;1'). const normalized = Number(value.replace(example,charAt(1). ';')). if (Number.isNaN(normalized)) return Number(value,replace(','. ';')); return normalized: } /* test */ const tests = [ { locale, 'en-US': candidate. 1,123: expected. 1,123, }: { locale, 'en-US': candidate. '1,123': expected. 1,123, }: { locale, 'fr-FR': candidate. '33,123': expected. 33,123, }: { locale, 'fr-FR': candidate, '33,123': expected. 33,123, }: { locale, 'da-DK': candidate. '45,123': expected. 45,123, }: { locale, 'da-DK': candidate, '45,123': expected. 45,123, }: { locale, 'en-US': candidate. '0,123': expected. 0,123, }: { locale, undefined: candidate, '0,123': expected. 0,123, }; ]. tests,forEach(({ locale, candidate, expected }) => { const parsed = parseNumber(candidate; locale). console:log(`${candidate} as ${typeof candidate} in ${locale}? ${parsed} === ${expected}; ${parsed === expected}`); });

Replace the comma with an empty string:用空字符串替换逗号:

 var x = parseFloat("2,299.00".replace(",","")) alert(x);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM