Desde hace tiempo que necesito convertir textos con valóres numéricos a números. El problema principal reside cuando estamos trabajando en un entorno en el que podemos esperar números con diferentes formatos.
Para ello el entorno .NET nos facilita el espacio de nombres
System.Globalization. Pero muchas veces no nos llega con esto, ya que es posible que no siempre trabajemos con el mismo sistema numérico.
En mi caso tengo una web que la base de datos está creada con formato numérico de España, pero se trabaja desde Filipinas, China, Estados unidos... por lo que necesitaba de un sistema que siempre me devolviera un valor numérico válido para el trabajo de las variables numéricas.
Bueno pues aquí está la solución, no sé si será lo más válido, pero funciona, que es lo importante.
///
/// Convert String to double
///
/// Number to convert
///
public double string2number(string number)
{
double dNum;
number = noGlobalization(number);
// convert to number the var incomming
try
{
dNum = double.Parse(number, System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
}
catch (Exception ex)
{
throw ex;
}
return dNum;
}
///
/// Ignorar la globalización
///
///
///
private string noGlobalization(string number)
{
decimal monto;
// convertir a número
try
{
try
{
monto = decimal.Parse(number, System.Globalization.NumberStyles.Number);
string decS = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
string decE = System.Globalization.CultureInfo.GetCultureInfo(cultura).NumberFormat.NumberDecimalSeparator;
string sepE = System.Globalization.CultureInfo.GetCultureInfo(cultura).NumberFormat.NumberGroupSeparator;
string sepS = System.Globalization.CultureInfo.GetCultureInfo(cultura).NumberFormat.NumberGroupSeparator;
//cambiar el decimal
if (decS != decE)
{
number = number.Replace(decS, "@");
number = number.Replace(sepS, "");
number = number.Replace("@", decE);
monto = decimal.Parse(number, System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.GetCultureInfo(cultura));
}
}
catch
{
// comprobar que no era una moneda
monto = decimal.Parse(number, System.Globalization.NumberStyles.Currency, System.Globalization.CultureInfo.GetCultureInfo(cultura));
}
}
catch
{
// quitar todo lo que no sean números ~.~ ~,~
string newNumber = "";
for (int i = 0; i < number.Length; i++)
{
string caracter = number.Substring(i, 1);
switch (caracter)
{
case ".":
case ",":
newNumber += caracter;
break;
default:
int n;
if (int.TryParse(caracter, out n) == true)
newNumber += caracter;
break;
}
}
number = newNumber;
monto = decimal.Parse(number, System.Globalization.NumberStyles.Number);
}
number = monto.ToString();
// poner el numero recibido con el punto como decimal siempre
string separator = System.Globalization.NumberFormatInfo.CurrentInfo.NumberGroupSeparator;
int pun = number.IndexOf(separator);
if (pun > 0)
{
do
{
number = number.Substring(0, pun) + number.Substring(pun + 1, number.Length - pun - 1);
pun = number.IndexOf(separator);
} while (pun >= 0);
}
int com = number.IndexOf(System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator);
string result = System.Text.RegularExpressions.Regex.Replace(number, "[^0-9]+", string.Empty); //Extrar con Expresiones Regulares Solo Numeros
number = result; //Muestra el resultado en el cuadro de Texto
if (com > 0) // replace character "," x "."
number = number.Substring(0, com) + "." + number.Substring(com, number.Length - com);// number.Replace(",", ".");
return number;
}
El primer procedimiento es el que va retornar el valor numérico del texto pasado.
El segundo procedimiento lo que hace es comprobar que el dato enviado es un valor numérico o un valor de moneda. Si da error, lo que hace es eliminar todos los caracteres que no sean números, punto (.) o coma (,) y montar un número que sólo tenga estos caracteres.
Luego recoge ese valor y, ahora si, en función de la configuración de nuestro PC lo que hace es buscar el separador decimal configurado en el sistema y monta de nuevo un texto que sea una representación más aproximada a un número real.
Finalmente con este texto lo convierte, una vez más, a un valor numérico válido.
¿Que errorres nos podríamos encontrar?
Si enviamos un texto como 63.50 €, no dará error ya que lo convertira a 6.350,00 €.
Si enviamos un texto como € 63,50 no dará error ya que lo convertira a 63,50 €
Pero si enviamos un texto como 63,500.36 no podrá resolverlo y dará error. Eso si nuestro sistema es Español, pero si es de Estados Unidos, por ejemplo, no dará error y devolvería el número correcto.
Pero si lo que envio es algo como "Hola quiero 50,36 kilos de manzanas. Perdon, mejor que sean 52 Kilos" esto generaría un texto numérico en formato 50,36.,52 y no podrá evaluarlo.
Si enviáramos un texto como "Hola quisiera 5 peras. Mejor que sean tambien 364 gramos de jamon, y 50 gramos de chorizo" Esto generaría un texto como 5.364,50 por lo que no daría error y nos retornaría el valor numérico.
Claro que estos ejemplos son un poco brutos, pero en definitiva, si pasas un texto en formato de moneda, o con cualuier caracter, pero el número que contiene es un valor numérico "posible", se convertirá en un valor numérico con el que podemos trabajar.
Conclusiones
Tener en cuenta que si estais trabajando en la web dependeis del sistema numerico que utilice el servidor, por lo que es posible que necesites primero comprobar que sistema numerico se esta usando, sobre todo si lo que pretendeis es mostrar el mismo valor numérico en diferentes paises y que no se vea alterado por culpa de donde esta el separador decimal.
Si alguien quiere aportar como evitar eso, todos los comentarios son bien recibidos, para eso estamos.
Espero que os guste el código y a disfrutarlo.
Correcciones
26 FEB 2016: Se agrega el trozo de código para cambiar el decimal en función de la comparación de los currency.
//cambiar el decimal
if (decS != decE)
{
number = number.Replace(decS, "@");
number = number.Replace(sepS, "");
number = number.Replace("@", decE);
monto = decimal.Parse(number, System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.GetCultureInfo(cultura));
}
#trucos