DEV Community

Cover image for Normalisation des Nombres pour les Données Internationales : Guide Pratique
circobit
circobit

Posted on

Normalisation des Nombres pour les Données Internationales : Guide Pratique

Vous exportez un tableau depuis un site allemand. La colonne revenus affiche « 1.234,56 € ».

Vous le collez dans Excel. Ça devient « 1.23456E » ou, pire, une date.

Bienvenue dans le problème des formats numériques internationaux.

Le Problème Central : Que Signifie « 1.234 » ?

Aux États-Unis et au Royaume-Uni : 1.234 = un virgule deux trois quatre (décimal)

En Allemagne, en Espagne et dans la plupart de l'Amérique latine : 1.234 = mille deux cent trente-quatre

Mêmes caractères. Valeurs complètement différentes.

Quand vous extrayez des données de sites internationaux, vous ne pouvez pas supposer quel format est utilisé. Et se tromper signifie que votre analyse est décalée de plusieurs ordres de grandeur.

L'Ambiguïté des Trois Chiffres

Le cas le plus difficile est exactement trois chiffres après un séparateur.

Entrée Interprétation US Interprétation EU
1.234 1,234 (décimal) 1 234 (milliers)
1,234 1 234 (milliers) 1,234 (décimal)
1.234.567 Invalide 1 234 567
1,234,567 1 234 567 Invalide

Avec deux chiffres après, c'est clairement décimal : « 1.23 » ou « 1,23 ».
Avec quatre chiffres ou plus, c'est clairement décimal : « 3.14159 ».
Avec trois chiffres ? Ça peut être l'un ou l'autre.

L'Algorithme de Détection

Voici une approche heuristique qui gère la plupart des cas réels :

function normalizeNumberString(value) {
  if (typeof value !== "string") return value;
  const v = value.trim();
  if (!v || !/[0-9]/.test(v)) return value;

  // Étape 1 : Nettoyer symboles monétaires, pourcentages, espaces
  let cleaned = v
    .replace(/ /g, " ")
    .replace(/^(USD|EUR|GBP|R\$|MXN)\s*/i, "")
    .replace(/[$€£¥₹₽₩]/g, "")
    .replace(/%/g, "")
    .replace(/\s+/g, "");

  // Étape 2 : Gérer les signes négatifs
  const isNegative = cleaned.match(/^-/);
  if (isNegative) {
    cleaned = cleaned.substring(1).trim();
  }

  // Étape 3 : Chiffres purs ? C'est terminé
  if (/^\d+$/.test(cleaned)) {
    return isNegative ? `-${cleaned}` : cleaned;
  }

  // Étape 4 : Détecter les séparateurs
  const hasComma = cleaned.includes(",");
  const hasDot = cleaned.includes(".");

  // Étape 5 : Appliquer l'heuristique
  let decimalSeparator = ".";
  let thousandsSeparator = ",";

  if (hasComma && hasDot) {
    // Les deux présents : le dernier est le décimal
    const lastComma = cleaned.lastIndexOf(",");
    const lastDot = cleaned.lastIndexOf(".");

    if (lastComma > lastDot) {
      decimalSeparator = ",";
      thousandsSeparator = ".";
    }
  } else if (hasComma && !hasDot) {
    const parts = cleaned.split(",");

    if (parts.length === 2 && parts[1].length <= 2) {
      // "1,23" → décimal
      decimalSeparator = ",";
    } else {
      // "1,234" ou "1,234,567" → milliers
      decimalSeparator = null;
      thousandsSeparator = ",";
    }
  } else if (hasDot && !hasComma) {
    const parts = cleaned.split(".");

    if (parts.length === 2) {
      const afterDot = parts[1];

      if (afterDot.length <= 2 || afterDot.length >= 4) {
        // "1.23" ou "3.14159" → décimal
        decimalSeparator = ".";
      } else {
        // "1.234" → MILLIERS (choix pragmatique pour les tableaux)
        decimalSeparator = null;
        thousandsSeparator = ".";
      }
    } else {
      // Points multiples → milliers
      decimalSeparator = null;
      thousandsSeparator = ".";
    }
  }

  // Étape 6 : Supprimer les séparateurs de milliers
  if (thousandsSeparator) {
    cleaned = cleaned.replace(new RegExp(`\\${thousandsSeparator}`, "g"), "");
  }

  // Étape 7 : Normaliser le décimal en point
  if (decimalSeparator && decimalSeparator !== ".") {
    cleaned = cleaned.replace(decimalSeparator, ".");
  }

  // Étape 8 : Valider
  const num = Number(cleaned);
  if (!Number.isFinite(num)) return value;

  return isNegative ? `-${cleaned}` : cleaned;
}
Enter fullscreen mode Exit fullscreen mode

Pourquoi Trois Chiffres = Milliers

La décision clé : quand on voit exactement trois chiffres après un seul séparateur (comme « 1.234 »), on interprète comme des milliers, pas comme un décimal.

Pourquoi ? Dans les tableaux HTML :

  • Les données financières avec des milliers sont extrêmement courantes : 1.234 $, 1.234 €
  • Les décimaux avec exactement trois chiffres sont rares en pratique
  • Les données scientifiques à trois décimales (comme « 3.141 ») sont généralement écrites « 3.14159 » ou simplement « 3.14 »

Cette heuristique est un compromis pragmatique qui fonctionne pour la majorité des tableaux réels.

La Matrice Complète des Formats

Entrée Interprétation Sortie Normalisée
1.234,56 Décimal EU 1234.56
1,234.56 Décimal US 1234.56
1.234.567,89 Milliers + décimal EU 1234567.89
1,234,567.89 Milliers + décimal US 1234567.89
1.234 Milliers 1234
1,234 Milliers 1234
3.14 Décimal 3.14
3,14 Décimal 3.14
3.14159 Décimal (4+ chiffres) 3.14159
$ 1.200,50 Devise + EU 1200.50
-5.000 Milliers négatifs -5000
12.5% Pourcentage 12.5

Gérer les Codes Devises

Les données internationales arrivent avec des préfixes et suffixes de devises. Nettoyez-les d'abord :

// Supprimer les codes devises AVANT les symboles
.replace(/^(USD|EUR|GBP|JPY|CHF|CAD|AUD|CNY|INR|BRL|R\$|MXN|KRW)\s*/i, "")
// Puis supprimer les symboles
.replace(/[$€£¥₹₽₩₪฿₫₴₦]/g, "")
Enter fullscreen mode Exit fullscreen mode

L'ordre compte : « R$ » (Real brésilien) contient « $ », donc supprimer les codes devises en premier évite les correspondances partielles.

Quand l'Heuristique Échoue

Aucune heuristique n'est parfaite. Vous obtiendrez des résultats incorrects quand :

  1. Données scientifiques avec exactement 3 décimales : « 3.141 » devient « 3141 »
  2. Prix sous 10 $ avec 3 décimales : « $1.234 » devient « $1234 »
  3. Formats mixtes dans une colonne : Certaines lignes EU, d'autres US

Pour le cas 3, vous avez besoin d'une détection au niveau de la colonne :

function detectColumnFormat(values) {
  let euIndicators = 0;
  let usIndicators = 0;

  for (const v of values) {
    if (/\d\.\d{3},\d{2}$/.test(v)) euIndicators++;
    if (/\d,\d{3}\.\d{2}$/.test(v)) usIndicators++;
  }

  if (euIndicators > usIndicators) return "eu";
  if (usIndicators > euIndicators) return "us";
  return "auto"; // Utiliser l'heuristique par cellule
}
Enter fullscreen mode Exit fullscreen mode

Profils d'Export pour Différentes Régions

Quand vous connaissez le pays source, vous pouvez éviter toute ambiguïté en utilisant des profils spécifiques à la région :

  • Format européen : Suppose la virgule comme séparateur décimal, utilise le point-virgule comme délimiteur CSV
  • Format US/UK : Suppose le point comme séparateur décimal, utilise la virgule comme délimiteur CSV

HTML Table Exporter inclut des profils prédéfinis pour les deux formats, plus des profils spécialisés pour des outils comme Pandas et DuckDB.

Pièges Courants

Ne Pas Perdre la Précision

// Faux : perd la précision sur les grands nombres
const num = parseFloat("12345678901234567890");
// 12345678901234567000 (limite JavaScript)

// Mieux : garder en chaîne jusqu'au calcul final
const cleaned = normalizeNumberString(value);
// Ne faire parseFloat que quand le calcul est nécessaire
Enter fullscreen mode Exit fullscreen mode

Attention aux Entités HTML

Les tableaux web contiennent parfois &nbsp; (espace insécable) dans les nombres :

// "1&nbsp;234&nbsp;567" devrait devenir "1234567"
.replace(/&nbsp;/g, " ")
.replace(/\s+/g, "")
Enter fullscreen mode Exit fullscreen mode

Préserver l'Original en Cas de Doute

Si la valeur ne ressemble pas à un nombre, retournez-la inchangée :

if (!/[0-9]/.test(v)) return value;
// ...
if (!Number.isFinite(num)) return value;
Enter fullscreen mode Exit fullscreen mode

Mieux vaut laisser « N/A » tel quel que d'essayer de le parser.

Résumé

Scénario Détection Notes
. et , présents Le dernier = décimal Universel
Virgule seule, ≤2 chiffres après Décimal « 1,23 »
Virgule seule, 3+ chiffres après Milliers « 1,234 »
Point seul, ≤2 ou 4+ chiffres Décimal « 1.23 », « 3.14159 »
Point seul, exactement 3 chiffres Milliers « 1.234 »

Le cas des trois chiffres est le compromis pragmatique. Ça fonctionne pour la plupart des données réelles. En cas de doute, utilisez un profil d'export correspondant à la région source.

Pour en savoir plus sur la copie de données depuis des sites web vers Excel sans problèmes de formatage, consultez notre guide sur la meilleure extension Chrome pour copier des tableaux dans Excel.


Besoin d'exports de nombres propres sans devinettes ? En savoir plus sur gauchogrid.com/fr/html-table-exporter ou essayez-le gratuitement sur le Chrome Web Store.

Top comments (0)