L'application traite des chaînes qui représentent des décimales provenant de différentes cultures. Par exemple, "1.1 et" 1,1 "ont la même valeur.
J'ai joué avec Decimal.TryParse
marque les combinaisons mais n'a pas pu obtenir le résultat souhaité. "1,1" est devenu "11" ou "0" après tout.
Est-il possible de convertir de telles chaînes en décimal en une seule ligne de code sans avoir à remplacer "," char en "." ou jouer avec NumberFormat.NumberDecimalSeparator
?
Comment gérez-vous de telles situations?
Merci d'avance!
Vous avez les possibilités suivantes:
Vous pouvez créer un objet CultureInfo
temporaire à utiliser lors de l'analyse.
// 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
J'ai trouvé une autre façon de le faire. Ça a l'air bizarre mais ça marche bien pour moi.
Donc, si vous ne connaissez pas la culture du système cible et que vous ne savez pas quelle valeur vous obtiendrez comme 12,33 ou 12,33, vous pouvez le faire en 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);
Vous avez juste besoin d'avoir la bonne culture définie, lorsque vous appelez 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);
Ci-dessous mon implémentation, une bonne idée?
/// <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 );
}
}
Bien, ce n'est toujours pas correct à 100%. lorsque vous utilisez le cas 1: vous supposez automatiquement que "," représente le chiffre décimal. vous devriez au moins vérifier s'il se produit plus d'une fois, car dans ce cas, c'est 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;
Utilisez TryParse deux fois avec 2 styles qui représentent deux possibilités
Si un seul retourne une valeur, utilisez cette valeur Si les deux renvoient une valeur, utilisez la moindre valeur en termes absolus.
TryParse renverra 0 pour les nombres qui utilisent à la fois le regroupement et le séparateur décimal si vous utilisez le mauvais style, mais si vous n'avez pas de séparateur de regroupement dans votre chaîne (par exemple, un nombre est inférieur à 1000), il renverra de la valeur dans les deux cas mais le "mauvais" nombre sera plus grand (encore une fois en termes absolus)