web-dev-qa-db-fra.com

Comment analyser une chaîne avec un séparateur de virgules mille en nombres?

J'ai 2,299.00 comme chaîne et j'essaie de l'analyser en un nombre. J'ai essayé d'utiliser parseFloat, ce qui donne 2. Je suppose que la virgule est le problème, mais comment pourrais-je résoudre ce problème correctement? Il suffit de retirer la virgule?

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

54
user1540714

Oui, supprimez les virgules:

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

Supprimer des virgules est potentiellement dangereux car, comme d'autres l'ont mentionné dans les commentaires, de nombreux paramètres régionaux utilisent une virgule pour signifier quelque chose de différent (comme une décimale).

Je ne sais pas d'où vient votre chaîne, mais dans certains endroits du monde "2,299.00" = 2.299

L'objet Intl aurait pu être un bon moyen de résoudre ce problème, mais ils ont néanmoins réussi à envoyer la spécification avec uniquement une API Intl.NumberFormat.format() et aucun homologue parse :(

Le seul moyen d’analyser une chaîne contenant des caractères numériques culturels en un numéro reconnaissable par une machine consiste à utiliser une bibliothèque qui exploite les données CLDR pour couvrir toutes les méthodes de formatage des chaînes numériques http: // cldr. unicode.org/

Les deux meilleures options JS que j'ai rencontrées jusqu'à présent:

47
David Meister

Supprimez tout ce qui n'est pas un chiffre, un point décimal ou un signe moins (-):

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

Violon mis à jour

Notez que cela ne fonctionnera pas pour les nombres en notation scientifique. Si vous le souhaitez, modifiez la ligne replace pour ajouter e, E et + à la liste des caractères acceptés:

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

Sur les navigateurs modernes, vous pouvez utiliser le Intl.NumberFormat intégré pour détecter le formatage du numéro du navigateur et normaliser l'entrée à mettre en correspondance.

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

Leur peut évidemment être optimisé et mis en cache, mais cela fonctionne de manière fiable dans toutes les langues.

20
Paul Alexander

En règle générale, vous devriez envisager d'utiliser des champs de saisie qui n'autorisent pas la saisie de texte libre pour les valeurs numériques. Mais il peut y avoir des cas où vous devez deviner le format d'entrée. Par exemple, 1.234.56 en Allemagne signifie 1.234.56 aux États-Unis. Voir https://salesforce.stackexchange.com/a/21404 pour obtenir une liste des pays qui utilisent des virgules comme décimales.

J'utilise la fonction suivante pour faire une meilleure estimation et supprimer tous les caractères non numériques:

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

Essayez-le ici: https://plnkr.co/edit/9p5Y6H?p=preview

Exemples:

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 fonction a un point faible: il est impossible de deviner le format si vous avez 1 123 ou 1,123 - car, en fonction du format de la langue, les deux peuvent être une virgule ou un séparateur de milliers. Dans ce cas particulier, la fonction traitera le séparateur comme un séparateur de milliers et renverra 1123.

11
Gerfried

C'est un wrapper simpliste et simpliste autour de la fonction 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);
}
2
Adam Jagosz

Toutes ces réponses échouent si vous avez un nombre de millions.

3 356 789 renverraient simplement 3456 avec la méthode replace.

La réponse la plus correcte pour supprimer simplement les virgules devrait être.

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

Ou simplement écrit. 

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

Il est étonnant qu'ils aient inclus une méthode toLocaleString mais pas une méthode parse . Au moins toLocaleString sans arguments est bien pris en charge dans IE6 +.

Pour une solution i18n , je suis venu avec ceci:

Commencez par détecter le séparateur décimal des paramètres régionaux de l'utilisateur:

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

Puis normalisez le nombre s'il y a plus d'un séparateur décimal dans la chaîne:

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

Enfin, supprimez tout ce qui n'est pas un nombre ou un séparateur décimal:

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

Si vous voulez éviter le problème publié par David Meister et que vous êtes certain du nombre de décimales, vous pouvez remplacer tous les points et les virgules et le diviser par 100, ex .:

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

ou si vous avez 3 décimales

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

C'est à vous de décider si vous souhaitez utiliser parseInt, parseFloat ou Number. Aussi, si vous souhaitez conserver le nombre de décimales, vous pouvez utiliser la fonction .toFixed (...).

0
Fernando Limeira

Remplacez la virgule par une chaîne vide: 

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

0
Aadit M Shah

Si vous avez un petit nombre de paramètres régionaux à prendre en charge, il serait probablement préférable de coder en dur quelques règles simples:

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

Si vous voulez une réponse, faites-le de cette façon. L'exemple utilise la monnaie, mais vous n'en avez pas besoin. La bibliothèque Intl devra être remplie si vous devez supporter les navigateurs plus anciens.

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