¿Cómo puedo analizar una cadena con un separador de miles de coma en un número?

103

Lo tengo 2,299.00como una cadena y estoy tratando de analizarlo en un número. Intenté usar parseFloat, lo que da como resultado 2. Supongo que la coma es el problema, pero ¿cómo podría resolver este problema de la manera correcta? ¿Solo quita la coma?

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

usuario1540714
fuente

Respuestas:

121

Sí, elimine las comas:

parseFloat(yournumber.replace(/,/g, ''));
Sam
fuente
7
Sí, pero ahora se han perdido los lugares decimales. 2300,00 da como resultado 2300, por ejemplo.
user1540714
@ user1540714 eso es porque es un flotador en lugar de una cadena. Si luego necesita generarlo, debe formatearlo para que siempre muestre 2 puntos decimales.
Jon Taylor
¿Se puede evitar eso? Intentaré arreglarlo
user1540714
38
Puedo sugerir que en su lugar capturemos todas las comas usando: .replace(/,/g, '') Esta expresión regular usa el global gpara reemplazar-todo.
gdibble
32
No tengo idea de por qué esta respuesta obtuvo tantos votos a favor y fue seleccionada como correcta, tiene dos problemas serios. 1. No puede manejar números con múltiples comas, por ejemplo, parseFloat("2,000,000.00".replace(',',''))devoluciones 2000y 2. falla en muchas regiones del mundo donde ,hay un lugar decimal, por ejemplo, parseFloat("2.000.000,00".replace(',',''))devoluciones 2- vea mi respuesta a continuación para algo que funciona en todas partes del mundo.
David Meister
113

Eliminar las comas es potencialmente peligroso porque, como otros han mencionado en los comentarios, muchas configuraciones regionales usan una coma para significar algo diferente (como un lugar decimal).

No sé de dónde sacaste tu cuerda, pero en algunos lugares del mundo "2,299.00"=2.299

El Intlobjeto podría haber sido una buena manera de abordar este problema, pero de alguna manera lograron enviar la especificación con solo una Intl.NumberFormat.format()API y sin parsecontraparte :(

La única forma de analizar una cadena con caracteres numéricos culturales en un número reconocible por la máquina de cualquier manera sana es usar una biblioteca que aproveche los datos CLDR para cubrir todas las formas posibles de formatear cadenas numéricas http: //cldr.unicode. org /

Las dos mejores opciones de JS que he encontrado para esto hasta ahora:

David Meister
fuente
4
No puedo creer que nadie haya votado a favor de esta respuesta todavía, ¡es la única respuesta real en esta página!
evilkos
9
Totalmente de acuerdo en que Intl debería haber tenido una contraparte de análisis. Parece obvio que la gente necesitaría esto.
Carlossless
Esta es la única forma sistemática de hacer esto. No se puede lograr esto con un enfoque de expresión regular para todos. Las comas y los puntos tienen diferentes significados en diferentes idiomas.
Wildhammer
38

En los navegadores modernos, puede utilizar el Intl.NumberFormat integrado para detectar el formato de número del navegador y normalizar la entrada para que coincida.

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

Obviamente, hay espacio para algunas optimizaciones y almacenamiento en caché, pero esto funciona de manera confiable en todos los idiomas.

Paul Alexander
fuente
¿Por qué esto no tiene un montón de votos a favor? ¡Es la solución más elegante para INPUT en formatos internacionales! ¡¡Gracias!!
kpollock
en Francia, la moneda es 23113413,12
stackdave
26

Quite todo lo que no sea un dígito, punto decimal o signo menos ( -):

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

Violín actualizado

Tenga en cuenta que no funcionará para números en notación científica. Si usted quiere que, cambiar la replacelínea para añadir e, Ey +la lista de caracteres aceptables:

str = str.replace(/[^\d\.\-eE+]/g, "");
TJ Crowder
fuente
4
Esto no funcionará para números negativos o números en notación científica.
Aadit M Shah
1
str = str.replace(/(\d+),(?=\d{3}(\D|$))/g, "$1"); Esto es lo que usaría, pero soy completamente inútil en regex y es algo que encontré hace un tiempo en algún otro hilo SO.
Jon Taylor
@JonTaylor: El objetivo aquí no era validar el número, solo hacer que funcionara parseFloat, lo que lo validará. :-)
TJ Crowder
lo que hace el mío, hasta donde yo sé. Utilizo esto para convertir 1,234,567, etc. a 1234567. Como dije, aunque soy completamente inútil en regex, por lo que no podría decirte lo que realmente hace jajaja.
Jon Taylor
1
mala idea eliminar "-" signo menos - obtener completamente otro número, y también si analiza formatos heterogéneos, "7.500"! == "7.500"
Serge
14

Por lo general, debe considerar el uso de campos de entrada que no permitan la entrada de texto libre para valores numéricos. Pero puede haber casos en los que necesite adivinar el formato de entrada. Por ejemplo, 1.234,56 en Alemania significa 1.234,56 en EE. UU. Consulte https://salesforce.stackexchange.com/a/21404 para obtener una lista de países que utilizan coma como decimal.

Utilizo la siguiente función para hacer una mejor estimación y eliminar todos los caracteres no numéricos:

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

Pruébelo aquí: https://plnkr.co/edit/9p5Y6H?p=preview

Ejemplos:

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 función tiene un punto débil: no es posible adivinar el formato si tiene 1.123 o 1.123, porque dependiendo del formato de la configuración regional, ambos pueden ser una coma o un separador de miles. En este caso especial, la función tratará el separador como un separador de miles y devolverá 1123.

Gerfried
fuente
Falla para números como 1,111.11, que obviamente es formato inglés, pero devuelve 111111
Mr.Goferito
Gracias señor Goferito - lo siento - arreglé la función.
Gerfried
Parece que esto también puede fallar para números muy pequeños "0,124" en la configuración regional francesa, por ejemplo.
Paul Alexander
¡Genial! Esto es casi exactamente lo que estaba buscando: 3,00 €también se reemplaza a 3.00. Solo una nota, que 3,001tiene el formato 3001. Para evitar esto, la entrada debe ser siempre con símbolos decimales. Por ejemplo, 3,001.00€ 3,001.00convierte correctamente. Además, actualice jsfiddle. 0,124todavía se convierte en 124
Arnis Juraga
bien hecho amigo, creo que vale la pena ser la respuesta correcta
Waheed
5

Es desconcertante que hayan incluido un método toLocaleString pero no un método de análisis . Al menos toLocaleString sin argumentos está bien soportado en IE6 +.

Para una solución i18n , se me ocurrió esto:

Primero detecte el separador decimal de la configuración regional del usuario:

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

Luego, normalice el número si hay más de un separador decimal en la Cadena:

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

Finalmente, elimine todo lo que no sea un número o un separador decimal:

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

Esta es una envoltura simplista y discreta alrededor de la parseFloatfunción.

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);
}
Adam Jagosz
fuente
2

Si desea evitar el problema que publicó David Meister y está seguro del número de decimales, puede reemplazar todos los puntos y comas y dividir por 100, ej .:

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

o si tienes 3 decimales

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

Depende de usted si desea utilizar parseInt, parseFloat o Number. Además, si desea mantener el número de lugares decimales, puede usar la función .toFixed (...).

Fernando Limeira
fuente
1

Todas estas respuestas fallan si tiene un número de millones.

3.456.789 simplemente devolvería 3456 con el método de reemplazo.

La respuesta más correcta para simplemente eliminar las comas tendría que ser.

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

O simplemente escrito.

number = parseFloat(number.split(',').join(''));
Caso
fuente
Por supuesto, los estadounidenses usan una coma y no un punto. Sería una tontería intentar hacer una monstruosidad para tratar de manejar ambos.
Caso
2
pero tal "monstruosidad" ya existe como parte de lo que proporciona Unicode (vea mi respuesta). Estoy seguro de que se sentiría menos tonto al respecto si dirigiera una empresa con clientes internacionales.
David Meister
1

Esto convierte un número en cualquier lugar en un número normal. También funciona para puntos decimales:

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
Eldar Gerfanov
fuente
1

Con esta función podrás formatear valores en múltiples formatos como 1.234,56y 1,234.56, e incluso con errores como 1.234.56y1,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"
joseantgv
fuente
0

Si quieres una respuesta l10n, hazlo de esta manera. El ejemplo usa moneda, pero no la necesita. La biblioteca de Intl deberá estar rellenada con polimerización si tiene que admitir navegadores más antiguos.

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, ""));
Tony Topper
fuente
9
esta no es realmente la respuesta l10n, porque para algunas configuraciones regionales (por ejemplo, DE) la coma es el punto decimal.
Andreas
Y también replace()solo reemplaza la primera coincidencia cuando no se usa RegExpcon 'g'flag.
binki
@binki Gracias. Fijo.
Tony Topper
@Andreas Esta es la respuesta l10n. Si quisiera otra moneda, simplemente cambiaría el valor de currencyId por el currencyId de ese país.
Tony Topper
1
@TonyTopper, esto no cambia el hecho de que el reemplazo del separador de miles está codificado en forma rígida y ,está en el separador de comas en algunos lugares
Andreas
0

Reemplaza la coma con una cadena vacía:

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

Aadit M Shah
fuente
esto no es seguro. Como se mencionó en otras respuestas, en muchas partes del mundo las comas representan un punto decimal.
David Meister
1
Además, este método no funcionaría en 2,299,000.00 ya que solo reemplazaría la primera coma
wizulus
0

Si tiene un pequeño conjunto de configuraciones regionales para admitir, probablemente estaría mejor codificando un par de reglas simples:

function parseNumber(str, locale) {
  let radix = ',';
  if (locale.match(/(en|th)([-_].+)?/)) {
    radix = '.';
  }
  return Number(str
    .replace(new RegExp('[^\\d\\' + radix + ']', 'g'), '')
    .replace(radix, '.'));
}
Andreas Baumgart
fuente
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, '.'));
};
Denys Rusov
fuente
0
Number("2,299.00".split(',').join(''));   // 2299

La función split divide la cadena en una matriz usando "," como separador y devuelve una matriz.
La función de unión une los elementos de la matriz devueltos por la función de división.
La función Number () convierte la cadena unida en un número.

Bruno
fuente
Explique un poco más cuál es la solución y cómo resuelve el problema.
Tariq,