149 votes

Comment analyser une chaîne de caractères avec un séparateur de milliers de virgules en un nombre ?

J'ai 2,299.00 comme une chaîne de caractères et j'essaie de l'analyser pour obtenir un nombre. J'ai essayé d'utiliser parseFloat Je suppose que la virgule est le problème, mais comment puis-je résoudre ce problème de la bonne manière ? Supprimer simplement la virgule ?

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

175voto

Sam Points 2028

Oui, enlevez les virgules :

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

168voto

David Meister Points 958

La suppression des virgules est potentiellement dangereuse car, comme d'autres l'ont mentionné dans les commentaires, de nombreuses localités utilisent une virgule pour signifier quelque chose de différent (comme une décimale).

Je ne sais pas d'où tu tiens ta ficelle, mais dans certains endroits du monde. "2,299.00" = 2.299

El Intl aurait pu être une bonne façon de résoudre ce problème, mais d'une manière ou d'une autre, ils ont réussi à livrer la spécification avec seulement un objet Intl.NumberFormat.format() et aucune parse homologue :(

La seule façon d'analyser une chaîne de caractères contenant des caractères numériques culturels pour obtenir un nombre reconnaissable par la machine d'une manière rationnelle en i18n est d'utiliser une bibliothèque qui exploite les données CLDR pour couvrir toutes les manières possibles de formater les chaînes de caractères numériques. http://cldr.unicode.org/

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

57voto

Paul Alexander Points 17611

Sur les navigateurs modernes, vous pouvez utiliser le module intégré Intl.NumberFormat pour détecter le formatage des nombres du navigateur et normaliser l'entrée pour qu'elle corresponde.

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

Il est évident qu'il est possible d'optimiser et de mettre en cache certains éléments, mais ce système fonctionne de manière fiable dans toutes les langues.

26voto

T.J. Crowder Points 285826

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

Mise à jour du violon

Notez que cela ne fonctionnera pas pour les nombres en notation scientifique. Si vous voulez qu'il le fasse, changez le paramètre replace ligne pour ajouter e , E et + à la liste des caractères acceptables :

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

19voto

Gerfried Points 1

En général, vous devriez envisager d'utiliser des champs de saisie qui ne permettent pas la saisie de texte libre pour les valeurs numériques. Mais il peut arriver que vous deviez deviner le format de saisie. Par exemple, 1.234,56 en Allemagne signifie 1.234.56 aux Etats-Unis. Voir https://salesforce.stackexchange.com/a/21404 pour une liste de pays qui utilisent la virgule comme décimale.

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 n'est pas possible de deviner le format si vous avez 1,123 ou 1.123 - parce que selon le format local, 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 retournera 1123.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X