2009-05-06 14 views
21

Ecco il mio problema (per en-US):problema C# Decimal.Parse con virgole

Decimal.Parse("1,2,3,4") rendimenti 1234, invece di lanciare un InvalidFormatException.

La maggior parte delle applicazioni Windows (Excel en-US) non rilasciano i mille separatori e non considerano tale valore un numero decimale. Lo stesso problema accade per altre lingue (anche se con caratteri diversi).

Esistono altre librerie di analisi decimali che risolvono questo problema?

Grazie!

+0

Stai cercando di non consentire il separatore di migliaia o solo quelli estranei? –

+0

Avrei dovuto essere più specifico (sono nuovo qui, mi dispiace!). Non riesco a separare migliaia di separatori nella stringa. "1.234.00" dovrebbe essere valido, mentre "12,34.00" dovrebbe essere errato. –

risposta

10

Ho finito per dover scrivere il codice per verificare manualmente la valuta. Personalmente, per un framework che si vanta di avere tutte le cose della globalizzazione integrate, è sorprendente che .NET non abbia nulla a che fare con questo.

La mia soluzione è qui sotto. Funziona per tutti i locali nel framework. Non supporta i numeri negativi, come tuttavia indicato da Orion in seguito. Che cosa ne pensate?

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); 
    } 

Ed ecco un test per dimostrare che funziona (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

Ci sono un paio di problemi in termini di utilizzo generale. Molte culture non usano - per rappresentare il segno negativo quindi includerlo nella regex non rende la cultura neutrale. Inoltre, alcuni sistemi di numerazione usano il segno negativo dopo il numero anziché davanti (ad es. "1234-" per "-1234" in en-US). Spesso devi anche consentire spazi prima o dopo il '-' per alcune culture. Anche alcune culture usano parentesi per numeri negativi che significano utilizzo abbinato. Ad ogni modo, se stai usando questo per en-US, allora dovresti stare bene. –

+2

Anche .Net supporta l'analisi della valuta. Devi solo passare le regole della valuta per l'opzione NumeroStati (cioè NumberStyles.Currency). Ha anche solo un'interpretazione più liberale del raggruppamento dei separatori. –

+0

Ciao Oron, grazie per il seguito. Hai ragione riguardo al segno negativo, semplicemente non funzionerebbe. Questo verrà utilizzato dagli utenti di tutto il mondo, ma l'applicazione non supporta l'input di valori negativi, quindi funziona per me. –

14

E 'consentito migliaia, perché il valore predefinito NumberStyles utilizzato da Decimal.Parse (NumberStyles.Number) include NumberStyles.AllowThousands.

Se si desidera disabilitare i separatori di migliaia, si può semplicemente rimuovere quella bandiera, in questo modo:

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

(? Il codice qui sopra genera un InvalidFormatException, che è ciò che si desidera, a destra)

+0

Non penso che sia quello che vuole. Penso che voglia che consideri "1.200" valido, ma non "1,2,0,0" ... potrebbe comunque essere sbagliato. –

+0

Hai ragione, Max. Ho ancora bisogno del separatore delle migliaia, quando separa solo migliaia. –

+0

Continuo a considerare il parser come difettoso. Il separatore dovrebbe essere visualizzato solo ogni tre cifre e solo a sinistra del punto decimale –

1

Potrebbe essere possibile eseguire questa operazione in un processo a due fasi. Per prima cosa è possibile verificare il separatore delle migliaia utilizzando le informazioni nello CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator e CultureInfo.CurrentCulture.NumberFormat.NumberGroupSizes generando un'eccezione se non passa e quindi passare il numero nello Decimal.Parse();

+0

Grazie, Orion, darò questa idea una prova. –

+4

Nome assolutamente fantastico! –

+0

Così è il tuo Orion! –

0

Si tratta di un problema comune mai risolto da Microsoft. Quindi, non capisco perché 1,2,3,00 (cultura inglese per esempio) sia valido! È necessario creare un algoritmo per esaminare le dimensioni del gruppo e restituire false/eccezioni (come un double.parse non riuscito) se il test non viene superato. Ho avuto un problema simile in un'applicazione mvc, che la compilazione nel validatore non accetta migliaia ... quindi l'ho sovrascritto con una custom, usando double/decimal/float.parse, ma aggiungendo una logica per convalidare la dimensione del gruppo.

Se volete leggere la mia soluzione (è usato per la mia MVC convalida personalizzata, ma è possibile utilizzarlo per avere una migliore doppia/decimale/float.parse validatore generico) vai qui https://stackoverflow.com/a/41916721/3930528