2009-05-06 36 views
21

Aquí está mi problema (por en-US):asunto C# Decimal.Parse por comas

Decimal.Parse("1,2,3,4") declaraciones de 1234, en vez de lanzar una InvalidFormatException.

mayoría de las aplicaciones de Windows (Excel en-US) no bajan de los separadores de miles y no consideran que el valor de un número decimal. El mismo problema ocurre con otros idiomas (aunque con diferentes caracteres).

¿Hay otras bibliotecas de análisis decimales que hay que resolver este problema?

Gracias!

+0

¿Estás tratando de no permitir ningún separador de miles, o solo los extraños? –

+0

Debería haber sido más específico (soy nuevo aquí, lo siento!). No puedo permitir miles de separadores en la cadena. "1,234.00" debería ser válido, mientras que "12,34.00" debería ser incorrecto. –

Respuesta

10

terminé tener que escribir el código para verificar la moneda de forma manual. Personalmente, para un framework que se enorgullece de tener todo el material de globalización incorporado, es increíble .NET no tiene nada para manejar esto.

Mi solución está por debajo. Funciona para todas las configuraciones regionales en el marco. No admite números Negativos, como Orion señaló a continuación, sin embargo. ¿Qué piensan ustedes?

public static bool TryParseCurrency(string value, out decimal result) 
    { 
     result = 0; 
     const int maxCount = 100; 
     if (String.IsNullOrEmpty(value)) 
      return false; 

     const string decimalNumberPattern = @"^\-?[0-9]{{1,{4}}}(\{0}[0-9]{{{2}}})*(\{0}[0-9]{{{3}}})*(\{1}[0-9]+)*$"; 

     NumberFormatInfo format = CultureInfo.CurrentCulture.NumberFormat; 

     int secondaryGroupSize = format.CurrencyGroupSizes.Length > 1 
       ? format.CurrencyGroupSizes[1] 
       : format.CurrencyGroupSizes[0]; 

     var r = new Regex(String.Format(decimalNumberPattern 
             , format.CurrencyGroupSeparator==" " ? "s" : format.CurrencyGroupSeparator 
             , format.CurrencyDecimalSeparator 
             , secondaryGroupSize 
             , format.CurrencyGroupSizes[0] 
             , maxCount), RegexOptions.Compiled | RegexOptions.CultureInvariant); 
     return !r.IsMatch(value.Trim()) ? false : Decimal.TryParse(value, NumberStyles.Any, CultureInfo.CurrentCulture, out result); 
    } 

Y aquí está una prueba para demostrar que funcione (nUnit):

[Test] 
    public void TestCurrencyStrictParsingInAllLocales() 
    { 
     var originalCulture = CultureInfo.CurrentCulture; 
     var cultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures); 
     const decimal originalNumber = 12345678.98m; 
     foreach(var culture in cultures) 
     { 
      var stringValue = originalNumber.ToCurrencyWithoutSymbolFormat(); 
      decimal resultNumber = 0; 
      Assert.IsTrue(DecimalUtils.TryParseCurrency(stringValue, out resultNumber)); 
      Assert.AreEqual(originalNumber, resultNumber); 
     } 
     System.Threading.Thread.CurrentThread.CurrentCulture = originalCulture; 

    } 
+1

Hay un par de problemas en términos de uso general. Muchas culturas no usan, para representar el signo negativo, por lo que incluirlo en tu expresión regular hace que no sea culturalmente neutral. Además, algunos sistemas de numeración usan el signo negativo después del número en lugar de al frente (por ejemplo, '1234-' para '-1234' en en-US). A menudo también debe permitir espacios antes o después del '-' para algunas culturas. Además, algunas culturas usan paréntesis para los números negativos, lo que significa uso emparejado. De cualquier manera, si solo está usando esto para en-US, entonces debería estar bien. –

+2

También .Net es compatible con el análisis de divisas. Solo tiene que pasar las reglas de moneda para la opción NumberStyles (es decir, NumberStyles.Currency). Simplemente también tiene una interpretación más liberal de los separadores de agrupación. –

+0

Hola Oron, gracias por el seguimiento. Tienes razón sobre el signo negativo, esto simplemente no funcionaría. Los usuarios de todo el mundo lo utilizarán, pero la aplicación no admite la entrada de valores negativos, por lo que funciona para mí. –

14

Está permitiendo que miles de personas, ya que el valor por defecto utilizado por NumberStylesDecimal.Parse (NumberStyles.Number) incluye NumberStyles.AllowThousands.

Si quiere desactivar los separadores de miles, sólo se puede eliminar esa bandera, de esta manera:

Decimal.Parse("1,2,3,4", NumberStyles.Number^NumberStyles.AllowThousands) 

(? El código anterior generará un InvalidFormatException, que es lo que desea, derecha)

+0

No creo que eso sea lo que él quiere. Creo que él quiere que considere "1,200" válido, pero no "1,2,0,0" ... podría estar equivocado. –

+0

Tienes razón, Max. Todavía necesito el separador de mil, cuando solo separa miles. –

+0

Todavía consideraría que el analizador es defectuoso. Solo se debe permitir que el separador aparezca cada tres dígitos y solo a la izquierda del punto decimal –

1

Es posible que pueda hacer esto en un proceso de dos fases. En primer lugar se podía verificar el separador de miles utilizando la información de la CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator y CultureInfo.CurrentCulture.NumberFormat.NumberGroupSizes lanzar una excepción si no pasa y luego pasar el número en el Decimal.Parse();

+0

Gracias, Orion, intentaré esta idea. –

+4

nombre totalmente increíble! –

+0

¡Así que es suyo Orion! –

0

Es un problema común que nunca resuelto por Microsoft. Entonces, no entiendo por qué 1,2,3.00 (cultura inglesa por ejemplo) ¡es válido! Necesita construir un algoritmo para examinar el tamaño del grupo y devolver falso/excepción (como un error double.parse) si la prueba no se pasa. Tuve un problema similar en una aplicación de mvc, que el validador de compilación no acepta miles ... así que lo sobreescribí con una costumbre, usando double/decimal/float.parse, pero agregué una lógica para validar el tamaño del grupo.

Si desea leer mi solución (que se utiliza para mi validador personalizado MVC, pero se puede utilizar para tener una mejor doble/decimal/float.parse validador genérico) ir aquí https://stackoverflow.com/a/41916721/3930528

Cuestiones relacionadas