2010-11-04 9 views
18

La aplicación trata con cadenas que representan decimales que provienen de diferentes culturas. Por ejemplo "1.1 y" 1,1 "es el mismo valor.La mejor manera de convertir cadena a separador decimal "." y "," manera insensible?

Jugué con Decimal.TryParse banderas de combinaciones pero no pude lograr el resultado que quiero." 1,1 "se convirtió en" 11 "o" 0 "después todos.

¿es posible convertir estas cadenas a decimal en una línea de código sin pre-sustitución "" char a "" o jugar con NumberFormat.NumberDecimalSeparator?

¿Cómo se maneja este tipo de situaciones?

¡Gracias de antemano!

+4

¿Cómo puede estar seguro de lo que ' 1,234' significa? – Kobi

+0

Es el valor del punto flotante entre 1 y 2 :) "1.234" es el mismo valor flotante –

+0

Concedió lo que Kobi publicó primero. 1.234 es uno y doscientos treinta y cuatro milésimos en francés, pero mil doscientos treinta y cuatro en inglés.Si no conoce la cultura de origen (como comentó a continuación), no puede determinar significativamente lo que originalmente se quiso decir. – dlanod

Respuesta

7

Tiene las siguientes possibllities:

  1. Usted sabe la cultura
    1. Use la configuración cultura actual, por lo que el equipo está instalado
    2. deja que el usuario decida establecer su cultura -> configuración del usuario en su programa
  2. Usted no conoce la cultura
    1. Debe decidir al respecto: hay que definir y documentar su decisión
    2. Guess: intenta analizar y tratar de analizar y tratar de ... hasta llegar números válidos
4

Usted sólo tiene que tener el conjunto de la cultura correcta, al llamar Parse, así:

string s = "11,20"; 

decimal c1 = decimal.Parse(s, new CultureInfo("fr-FR")); 
decimal c2 = decimal.Parse(s, new CultureInfo("en-AU")); 

Console.WriteLine(c1); 
Console.WriteLine(c2); 
+0

Pero no conozco la cultura original :( –

+2

Debe asegurarse de conocer la cultura original; de lo contrario, no puede decidir qué significa el número. –

+0

¿Qué sucede si el valor proviene de un formulario? Reemplace ',' s con '.s antes de analizar? (editar: bien, se le pidió que no se utilizara, pero aún así ...) – Bertvan

32

Se puede crear un objeto temporal CultureInfo utilizar al analizar.

// get a temporary culture (clone) to modify 
var ci = CultureInfo.InvariantCulture.Clone() as CultureInfo; 
ci.NumberFormat.NumberDecimalSeparator = ","; 
decimal number = decimal.Parse("1,1", ci); // 1.1 
+0

No, gracias Creo que ya tenemos suficientes culturas ':)' – Kobi

+0

Bien, más en serio: casi nunca desea usar 'CurrentCulture' en el servidor; no sabe cómo se configura. – Kobi

+1

¿Qué tal InvariantCulture con NumberDecimalSeparator override? –

3

A continuación se muestra mi implementación, ¿alguna buena idea?

/// <summary> 
/// 
/// </summary> 
public static class NumberExtensions 
{ 
    /// <summary> 
    /// Convert string value to decimal ignore the culture. 
    /// </summary> 
    /// <param name="value">The value.</param> 
    /// <returns>Decimal value.</returns> 
    public static decimal ToDecimal (this string value) 
    { 
     decimal number; 
     string tempValue = value; 

     var punctuation = value.Where (x => char.IsPunctuation (x)).Distinct (); 
     int count = punctuation.Count (); 

     NumberFormatInfo format = CultureInfo.InvariantCulture.NumberFormat; 
     switch (count) 
     { 
      case 0: 
       break; 
      case 1: 
       tempValue = value.Replace (",", "."); 
       break; 
      case 2: 
       if (punctuation.ElementAt (0) == '.') 
        tempValue = value.SwapChar ('.', ','); 
       break; 
      default: 
       throw new InvalidCastException (); 
     } 

     number = decimal.Parse (tempValue, format); 
     return number; 
    } 
    /// <summary> 
    /// Swaps the char. 
    /// </summary> 
    /// <param name="value">The value.</param> 
    /// <param name="from">From.</param> 
    /// <param name="to">To.</param> 
    /// <returns></returns> 
    public static string SwapChar (this string value, char from, char to) 
    { 
     if (value == null) 
      throw new ArgumentNullException ("value"); 

     StringBuilder builder = new StringBuilder (); 

     foreach (var item in value) 
     { 
      char c = item; 
      if (c == from) 
       c = to; 
      else if (c == to) 
       c = from; 

      builder.Append (c); 
     } 
     return builder.ToString (); 
    } 
} 

[TestClass] 
public class NumberTest 
{ 

    /// <summary> 
    /// 
    /// </summary> 
    [TestMethod] 
    public void Convert_To_Decimal_Test () 
    { 
     string v1 = "123.4"; 
     string v2 = "123,4"; 
     string v3 = "1,234.5"; 
     string v4 = "1.234,5"; 
     string v5 = "123"; 
     string v6 = "1,234,567.89"; 
     string v7 = "1.234.567,89"; 

     decimal a1 = v1.ToDecimal (); 
     decimal a2 = v2.ToDecimal (); 
     decimal a3 = v3.ToDecimal (); 
     decimal a4 = v4.ToDecimal (); 
     decimal a5 = v5.ToDecimal (); 
     decimal a6 = v6.ToDecimal (); 
     decimal a7 = v7.ToDecimal (); 

     Assert.AreEqual ((decimal) 123.4, a1); 
     Assert.AreEqual ((decimal) 123.4, a2); 
     Assert.AreEqual ((decimal) 1234.5, a3); 
     Assert.AreEqual ((decimal) 1234.5, a4); 
     Assert.AreEqual ((decimal) 123, a5); 
     Assert.AreEqual ((decimal) 1234567.89, a6); 
     Assert.AreEqual ((decimal) 1234567.89, a7); 
    } 
    /// <summary> 
    /// 
    /// </summary> 
    [TestMethod] 
    public void Swap_Char_Test () 
    { 
     string v6 = "1,234,567.89"; 
     string v7 = "1.234.567,89"; 

     string a1 = v6.SwapChar (',', '.'); 
     string a2 = v7.SwapChar (',', '.'); 

     Assert.AreEqual ("1.234.567,89", a1); 
     Assert.AreEqual ("1,234,567.89", a2); 
    } 
} 
2

bueno, todavía no es 100% correcto. cuando usa la caja 1: supone automáticamente que ',' significa dígito decimal. al menos debe comprobar si occures más de una vez, causan en ese caso, es un grupo de símbolos que separa

  case 1: 
       var firstPunctuation = linq.ElementAt(0); 
       var firstPunctuationOccurence = value.Where(x => x == firstPunctuation).Count(); 

       if (firstPunctuationOccurence == 1) 
       { 
        // we assume it's a decimal separator (and not a group separator) 
        value = value.Replace(firstPunctuation.ToString(), format.NumberDecimalSeparator); 
       } 
       else 
       { 
        // multiple occurence means that symbol is a group separator 
        value = value.Replace(firstPunctuation.ToString(), format.NumberGroupSeparator); 
       } 

       break; 
+0

y ¿qué pasa con el caso, donde la coma (,) está allí solo una vez, pero sigue siendo un símbolo de separación de grupos? por ejemplo, "111,222" que significa 111222.0 –

11

he encontrado otra manera de hacerlo. Parece extraño pero funciona bien para mí.

Así que si usted no sabe la cultura del sistema de destino y no se sabe cual es el valor que obtendrá como 12,33 o 12,33 Usted puede hacer siguiendo

string amount = "12.33"; 
// or i.e. string amount = "12,33"; 

var c = System.Threading.Thread.CurrentThread.CurrentCulture; 
var s = c.NumberFormat.CurrencyDecimalSeparator; 

amount = amount.Replace(",", s); 
amount = amount.Replace(".", s); 

decimal transactionAmount = Convert.ToDecimal(amount); 
+0

Las cadenas son peligrosas, ¿posible problema con el separador de mil? 1.2345,40? – Evilripper

Cuestiones relacionadas