Come posso analizzare una stringa con un separatore virgola mille in un numero?


104

Ho 2,299.00una stringa e sto cercando di analizzarla con un numero. Ho provato a usare parseFloat, il che si traduce in 2. Immagino che la virgola sia il problema, ma come potrei risolvere questo problema nel modo giusto? Basta rimuovere la virgola?

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

Risposte:


121

Sì, rimuovi le virgole:

parseFloat(yournumber.replace(/,/g, ''));

7
Sì, ma ora le cifre decimali sono perse. 2300.00 risulta in 2300 per esempio.
user1540714

@ user1540714 questo è perché è un float piuttosto che una stringa. Se hai bisogno di emetterlo, devi formattarlo per mostrare sempre 2 punti decimali.
Jon Taylor

Può essere evitato? Proverò aFixed
user1540714

38
Posso suggerire di catturare invece tutte le virgole usando: .replace(/,/g, '') Questa regex usa il globale gper sostituire tutto.
gdibble

32
Non ho idea del motivo per cui questa risposta abbia ricevuto così tanti voti positivi ed è stata selezionata come corretta, ha due problemi seri. 1. Non è in grado di gestire numeri con più virgole, ad es. parseFloat("2,000,000.00".replace(',',''))Ritorni 2000e 2. fallisce in molte regioni del mondo dove ,è presente una cifra decimale, ad es. parseFloat("2.000.000,00".replace(',',''))Ritorni 2- vedi la mia risposta sotto per qualcosa che funziona ovunque nel mondo.
David Meister

113

La rimozione delle virgole è potenzialmente pericolosa perché, come altri hanno menzionato nei commenti, molte lingue usano una virgola per indicare qualcosa di diverso (come un punto decimale).

Non so da dove hai preso la tua stringa, ma in alcuni posti nel mondo "2,299.00"=2.299

L' Intloggetto avrebbe potuto essere un bel modo per affrontare questo problema, ma in qualche modo sono riusciti a spedire le specifiche con solo Intl.NumberFormat.format()un'API e nessuna parsecontroparte :(

L'unico modo per analizzare una stringa con caratteri numerici culturali in un numero riconoscibile dalla macchina in qualsiasi modo sensato di i18n è usare una libreria che sfrutta i dati CLDR per coprire tutti i possibili modi di formattare le stringhe numeriche http: //cldr.unicode. org /

Le due migliori opzioni JS che ho incontrato finora:


4
Non posso credere che nessuno abbia ancora votato questa risposta, è l'unica risposta effettiva in questa pagina!
evilkos

9
Completamente d'accordo sul fatto che Intl avrebbe dovuto avere una controparte di analisi. Sembra ovvio che le persone ne avrebbero bisogno.
carlossless

Questo è l'unico modo sistematico per farlo. Non è possibile ottenere questo risultato con un approccio regex adatto a tutti. Le virgole e i punti hanno significati diversi nelle diverse lingue.
Wildhammer,

38

Sui browser moderni è possibile utilizzare l' Intl.NumberFormat integrato per rilevare la formattazione del numero del browser e normalizzare l'input in modo che corrisponda.

function parseNumber(value, locale = navigator.language) {
  const example = Intl.NumberFormat(locale).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 }`);
}

Ovviamente c'è spazio per l'ottimizzazione e la memorizzazione nella cache, ma funziona in modo affidabile in tutte le lingue.


Perché questo non ha ottenuto un carico di voti positivi !!! È la soluzione più elegante per INPUT nei formati internazionali! Grazie!!
kpollock

in Francia la valuta è 231 123 413,12
stackdave

26

Rimuovi tutto ciò che non è una cifra, un punto decimale o un segno meno ( -):

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

Violino aggiornato

Nota che non funzionerà per i numeri in notazione scientifica. Se si desidera, cambiare la replacelinea per aggiungere e, Ee +l'elenco dei caratteri accettabili:

str = str.replace(/[^\d\.\-eE+]/g, "");

4
Questo non funzionerà per numeri negativi o numeri in notazione scientifica.
Aadit M Shah

1
str = str.replace(/(\d+),(?=\d{3}(\D|$))/g, "$1"); Questo è quello che userei ma sono completamente inutile alle espressioni regolari ed è qualcosa che ho trovato tempo fa su qualche altro thread SO.
Jon Taylor

@ JonTaylor: l'obiettivo qui non era convalidare il numero, ma solo farlo funzionare parseFloat, il che lo convaliderà. :-)
TJ Crowder

che il mio fa, per quanto ne so. Lo uso per convertire 1.234.567 ecc. In 1234567. Come ho detto, anche se sono completamente inutile alle espressioni regolari quindi non potrei per la vita di me dirti cosa fa effettivamente lol.
Jon Taylor

1
pessima idea rimuovere "-" segno meno - ottenere completamente un altro numero, e anche se si analizzano formati eterogenei, "7.500"! == "7.500"
Serge

14

Di solito dovresti considerare di utilizzare campi di input che non consentono l'immissione di testo libero per valori numerici. Ma potrebbero esserci casi in cui è necessario indovinare il formato di input. Ad esempio 1.234,56 in Germania significa 1.234.56 negli Stati Uniti. Vedere https://salesforce.stackexchange.com/a/21404 per un elenco di paesi che utilizzano la virgola come decimale.

Uso la seguente funzione per fare un'ipotesi migliore e rimuovere tutti i caratteri non numerici:

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);
}   

Provalo qui: https://plnkr.co/edit/9p5Y6H?p=preview

Esempi:

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

La funzione ha un punto debole: non è possibile indovinare il formato se si dispone di 1.123 o 1.123, perché a seconda del formato locale entrambi potrebbero essere una virgola o un separatore delle migliaia. In questo caso speciale la funzione tratterà separator come un separatore di migliaia e restituirà 1123.


Non riesce per numeri come 1.111.11, che è ovviamente in formato inglese, ma restituisce 111111
Mr.Goferito

Grazie, signor Goferito - mi dispiace - ho sistemato la funzione.
Gerfried

Sembra che questo possa fallire anche per numeri molto piccoli "0,124" nella lingua francese, ad esempio.
Paul Alexander

Fantastico! Questo è quasi esattamente quello che stavo cercando: 3,00 €è anche sostituito a 3.00. Solo una nota, che 3,001è formattata in 3001. Per evitare ciò, l'immissione dovrebbe sempre essere con simboli decimali. Ad esempio, 3,001.00€ 3,001.00converte correttamente. Inoltre, per favore, aggiorna jsfiddle. 0,124c'è ancora convertito in 124
Arnis Juraga

bravo amico, penso che valga la pena essere la risposta corretta
Waheed

5

È sconcertante che abbiano incluso un toLocaleString ma non un metodo di analisi . Almeno toLocaleString senza argomenti è ben supportato in IE6 +.

Per un i18n soluzione , ho a questo:

Per prima cosa rileva il separatore decimale delle impostazioni internazionali dell'utente:

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

Quindi normalizza il numero se c'è più di un separatore decimale nella stringa:

var pattern = "([" + decimalSeparator + "])(?=.*\\1)";separator
var formatted = valor.replace(new RegExp(pattern, "g"), "");

Infine, rimuovi tutto ciò che non è un numero o un separatore decimale:

formatted = formatted.replace(new RegExp("[^0-9" + decimalSeparator + "]", "g"), '');
return Number(formatted.replace(decimalSeparator, "."));

5

Questo è un semplice involucro discreto attorno alla parseFloatfunzione.

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);
}

2

Se vuoi evitare il problema postato da David Meister e sei sicuro del numero di cifre decimali, puoi sostituire tutti i punti e le virgole e dividere per 100, es .:

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

o se hai 3 decimali

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

Sta a te decidere se vuoi usare parseInt, parseFloat o Number. Inoltre, se vuoi mantenere il numero di cifre decimali puoi usare la funzione .toFixed (...).


1

Tutte queste risposte falliscono se hai un numero di milioni.

3.456.789 restituirebbe semplicemente 3456 con il metodo di sostituzione.

La risposta più corretta per rimuovere semplicemente le virgole dovrebbe essere.

var number = '3,456,789.12';
number.split(',').join('');
/* number now equips 3456789.12 */
parseFloat(number);

O semplicemente scritto.

number = parseFloat(number.split(',').join(''));

Ovviamente, l'americano usa virgola e non punto. Sarebbe sciocco provare a fare una mostruosità per tentare di gestire entrambi.
Causa

2
ma una tale "mostruosità" esiste già come parte di ciò che fornisce Unicode (vedi la mia risposta). Sono sicuro che ti sentiresti meno stupido se gestissi un'azienda con clienti internazionali.
David Meister

1

Questo converte un numero in qualsiasi locale in un numero normale. Funziona anche per i punti decimali:

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],"."));
}
//Use default browser locale
numberFromLocaleString("1,223,333.567") //1223333.567

//Use specific locale
numberFromLocaleString("1 223 333,567", "ru") //1223333.567

1

Con questa funzione sarai in grado di formattare i valori in più formati come 1.234,56e 1,234.56, e anche con errori come 1.234.56e1,234,56

/**
 * @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);
}
parseFloatFromString('1.234,56')
"1234.56"
parseFloatFromString('1,234.56')
"1234.56"
parseFloatFromString('1.234.56')
"1234.56"
parseFloatFromString('1,234,56')
"1234.56"

0

Se vuoi una risposta l10n fallo in questo modo. L'esempio usa la valuta, ma non ne hai bisogno. La libreria Intl dovrà essere polyfilled se devi supportare browser meno recenti.

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, ""));

9
questa non è veramente una risposta l10n, perché per alcune versioni locali (ad esempio DE) la virgola è il punto decimale.
Andreas

Inoltre, replace()sostituisce solo la prima corrispondenza quando non si utilizza RegExpcon 'g'flag.
binki

@binki Grazie. Fisso.
Tony Topper

@Andreas Questa è la risposta l10n. Se volessi un'altra valuta, cambierai semplicemente il valore di currencyId in quel paese currencyId.
Tony Topper

1
@TonyTopper questo non cambia il fatto che il sostituto per il separatore delle migliaia è codificato in modo hardcoded ,in un separatore virgola in alcune versioni locali
Andreas

0

Sostituisci la virgola con una stringa vuota:

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


questo non è sicuro. Come accennato in altre risposte, in molte parti del mondo le virgole rappresentano un punto decimale.
David Meister

1
Anche questo metodo non funzionerebbe su 2.299.000,00 in quanto sostituirebbe solo la prima virgola
wizulus

0

Se hai un piccolo set di localizzazioni da supportare, probabilmente staresti meglio semplicemente codificando un paio di semplici regole:

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

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

0
Number("2,299.00".split(',').join(''));   // 2299

La funzione split divide la stringa in un array utilizzando "," come separatore e restituisce un array.
La funzione join unisce gli elementi dell'array restituiti dalla funzione split.
La funzione Number () converte la stringa unita in un numero.


Si prega di elaborare un po 'di più su qual è la soluzione e su come risolve il problema.
Tariq
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.