简体   繁体   中英

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. 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? 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).

On modern browsers you can use the built in Intl.NumberFormat to detect the browser's number formatting and normalize the input to match.

 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> ):

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. See https://salesforce.stackexchange.com/a/21404 for a list of countries which use comma as decimal.

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

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. In this special case the function will treat separator as a thousands-separator and return 1123.

It's baffling that they included a toLocaleString<\/strong> but not a parse<\/strong> method. At least toLocaleString<\/strong> without arguments is well supported in IE6+.

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

This is a simplistic unobtrusive wrapper around the parseFloat function.

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.:

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.

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:

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.

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>

/**
 * @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. Example uses currency, but you don't need that. Intl library will need to be polyfilled if you have to support older browsers.

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.

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(',', '.'));

If this is used in a form, I found that I should use inputMode="numeric" rather than 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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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