2010-11-04 4 views
18

application traite des chaînes qui représentent décimaux qui viennent de cultures différentes. Par exemple « 1.1 et « 1,1 » est la même valeur.Meilleure façon de convertir la chaîne en séparateur décimal "." et "," manière insensible?

J'ai joué avec des combinaisons Decimal.TryParse drapeaux mais n'a pas pu obtenir le résultat que je veux. « 1,1 » est devenu « 11 » ou « 0 » après tous.

Est-il possible de convertir ces chaînes en décimal dans une ligne de code sans pré-remplacement « » char « » ou jouer avec NumberFormat.NumberDecimalSeparator?

Comment gérez-vous ces situations?

Merci à l'avance!

+4

Comment pouvez-vous être sûr de ce que ' 1,234' signifie? – Kobi

+0

Il est la valeur en virgule flottante entre 1 et 2 :) « 1.234 » est la même valeur flottante –

+0

Appuyée ce que Kobi a affiché la première place. 1234 est un et deux cent trente-quatre millièmes en français mais mille deux cent trente quatre en anglais.Si vous ne connaissez pas la culture source (comme vous l'avez commenté ci-dessous), vous ne pouvez pas déterminer de façon significative ce que l'on voulait dire à l'origine. – dlanod

Répondre

7

Vous avez les possibllities suivants:

  1. Vous connaissez la culture
    1. Utilisez le réglage de la culture actuelle, pour lequel l'ordinateur est installé
    2. Vous permettre à l'utilisateur de décider de mettre sa culture -> paramètres utilisateur dans votre programme
  2. vous ne connaissez pas la culture
    1. Vous devez décider: vous devez définir et documenter votre décision
    2. Devinez: vous essayez d'analyser et essayer d'analyser et essayer de ... jusqu'à ce que vous obtenez des numéros valides
4

Vous avez juste besoin d'avoir l'ensemble de la culture correcte, lors de l'appel Parse, comme ceci:

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

Mais je ne connais pas la culture originale :( –

+2

Vous devez vous assurer que vous connaissez la culture d'origine, sinon vous ne pouvez pas décider ce que le nombre est censé indiquer –

+0

Que faire si la valeur provient d'un formulaire? Remplacer ',' s par '.s avant d'analyser? (edit: okay qui a été demandé de ne pas utiliser, mais quand même ...) – Bertvan

32

Vous pouvez créer un objet temporaire CultureInfo à utiliser lorsque vous analysez.

// 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

Non merci, Je pense que nous avons déjà assez de cultures ':)' – Kobi

+0

Ok, plus sérieusement - vous ne voulez presque jamais utiliser CurrentCulture sur le serveur - vous ne savez pas comment il est défini. – Kobi

+1

Que diriez-vous de InvariantCulture avec le remplacement NumberDecimalSeparator? –

3

ci-dessous est ma mise en œuvre, tout bon idear?

/// <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

bien, ce n'est toujours pas correct à 100%. lorsque vous utilisez le cas 1: vous supposez automatiquement, que « » est synonyme de chiffre décimal. vous devriez au moins vérifier s'il se produit plus d'une fois, cause dans ce cas son un symbole de séparation de groupe

  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

et qu'en est-il du cas, où la virgule (,) est là une seule fois, mais c'est toujours un symbole de séparation de groupe? par exemple "111,222" signifiant 111222.0 –

11

J'ai trouvé une autre façon de le faire. Ça a l'air bizarre mais ça fonctionne très bien pour moi.

Donc, si vous ne connaissez pas la culture du système cible et vous ne savez pas quelle valeur vous obtiendrez comme 12,33 ou 12,33, vous pouvez faire suivant

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

Les cordes sont dangereuses, problème possible avec le séparateur de milliers? 1.2345,40? – Evilripper

Questions connexes