2010-01-31 8 views
24

Ho una stringa come "1.5%" e voglio convertirlo in doppio valore.Come convertire la stringa di percentuale in doppio?

può essere fatto semplice con i seguenti:

public static double FromPercentageString(this string value) 
{ 
    return double.Parse(value.SubString(0, value.Length - 1))/100; 
} 

ma non voglio usare questo approccio di analisi.

C'è qualche altro approccio con IFormatProvider o qualcosa del genere?

risposta

38

Se si cura di rilevare errori di formattazione, utilizzare TrimEnd anziché Sostituisci. Sostituisci consentirebbe errori di formattazione passare inosservato.

var num = decimal.Parse(value.TrimEnd(new char[] { '%', ' ' }))/100M; 

Questo farà sì che il valore deve essere un certo numero decimale seguito da un numero qualsiasi di spazi e segni di percentuale, cioè, essa deve almeno iniziare con un valore nel formato corretto. Per essere più precisi, potresti voler dividere in "%", non rimuovere le voci vuote, quindi assicurarti che ci siano solo due risultati e il secondo sia vuoto. Il primo dovrebbe essere il valore da convertire.

var pieces = value.Split('%'); 
if (pieces.Length > 2 || !string.IsNullOrEmpty(pieces[1])) 
{ 
    ... some error handling ... 
} 
var num = decimal.Parse(pieces[0])/100M; 

Utilizzando Sostituisci vi permetterà di successo, e ingiustamente IMO, analizza le cose come:

  • % 1,5
  • 1% .5
  • 1.% 5

in aggiunta a 1.5%

+0

Puoi usare 'if (value.EndsWith ("% ")) ...' – Bitterblue

+1

Alcune colture iniziano con percentuali, quindi hai davvero bisogno di CultureInfo.CurrentCulture.NumberFormat –

-1

È una stringa, non importa cosa fai con essa per rimuovere il segno% che devi ancora analizzare per raddoppiare.

+2

-1 Questa non è una risposta alla domanda. –

+0

@ C.Ross, in realtà è una risposta ed è quella giusta, se effettivamente ti preoccupi di leggere la domanda, vedresti che non "vuole usare questo approccio di parsing", quando è in realtà, l'unico approccio tranne forse Convert.ToDouble che sarebbe equivalente. –

+0

A quanto ho capito, l'OP si riferisce all'approccio di parsing _particular_ che aveva nel suo post. Significato del bit tra le parentesi - non l'uso effettivo del metodo parse. Anche se consideri il tuo contributo una risposta, sfortunatamente trovo che non sia molto utile (-1). Suggerisco che invece di dire "non importa cosa fai con esso per rimuovere il segno%" che espandi questo con diverse opzioni su come gestire il segno%. Questo è il punto cruciale della domanda dell'OP e anche il motivo per cui sono finito su questa pagina. Ciò aiuterebbe anche gli altri che vengono qui in cerca di risposte. – Ben

9

solo leggermente migliore, ma meno incline all'errore:

public static double FromPercentageString(this string value) 
{ 
    return double.Parse(value.Replace("%",""))/100; 
} 
38

È cultura sensibile, sostituirlo simili:

value = value.Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol, ""); 

analizzarlo.

+0

+1 Soluzione meravigliosamente semplice –

+0

Direi che ci sono (tecnicamente) casi non gestiti qui. Vedi la mia risposta. – sammy34

+1

Utilizzato come metodo di estensione per la stringa: stringa statica pubblica RemovePercentageSign (stringa str) { return str.Replace (System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol, string.Empty); } –

8

TypeConverter fornisce un metodo unificato per convertire tipi di valori in altri tipi, nonché per accedere a valori e sottoproprietà standard. http://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter%28VS.80%29.aspx

Questo è probabilmente eccessivo per le conversioni una tantum. È molto più utile quando si legano le proprietà in ASP.NET o XAML o quando si analizzano i file di configurazione.

var result = new Percentage("1.5%"); 
double d = result.Value; 

Percentuale e la sua TypeConverter sono definiti come:

[TypeConverter(typeof(PercentageConverter))] 
public struct Percentage 
{ 
    public double Value; 

    public Percentage(double value) 
    { 
     Value = value; 
    } 

    public Percentage(string value) 
    { 
     var pct = (Percentage) TypeDescriptor.GetConverter(GetType()).ConvertFromString(value); 
     Value = pct.Value; 
    } 

    public override string ToString() 
    { 
     return ToString(CultureInfo.InvariantCulture); 
    } 

    public string ToString(CultureInfo Culture) 
    { 
     return TypeDescriptor.GetConverter(GetType()).ConvertToString(null, Culture, this); 
    } 
} 

public class PercentageConverter : TypeConverter 
{ 
    static TypeConverter conv = TypeDescriptor.GetConverter(typeof(double)); 

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) 
    { 
     return conv.CanConvertFrom(context, sourceType); 
    } 

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) 
    { 
     if (destinationType == typeof(Percentage)) { 
      return true; 
     } 

     return conv.CanConvertTo(context, destinationType); 
    } 

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) 
    { 
     if (value == null) { 
      return new Percentage(); 
     } 

     if (value is string) { 
      string s = value as string; 
      s = s.TrimEnd(' ', '\t', '\r', '\n'); 

      var percentage = s.EndsWith(culture.NumberFormat.PercentSymbol); 
      if (percentage) { 
       s = s.Substring(0, s.Length - culture.NumberFormat.PercentSymbol.Length); 
      } 

      double result = (double) conv.ConvertFromString(s); 
      if (percentage) { 
       result /= 100; 
      } 

      return new Percentage(result); 
     } 

     return new Percentage((double) conv.ConvertFrom(context, culture, value)); 
    } 

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) 
    { 
     if (!(value is Percentage)) { 
      throw new ArgumentNullException("value"); 
     } 

     var pct = (Percentage) value; 

     if (destinationType == typeof(string)) { 
      return conv.ConvertTo(context, culture, pct.Value * 100, destinationType) + culture.NumberFormat.PercentSymbol; 
     } 

     return conv.ConvertTo(context, culture, pct.Value, destinationType); 
    } 
} 
+4

+1. IMHO è la migliore risposta per questa domanda.Oltre al tuo codice, aggiungerei anche due operatori di conversione impliciti per un codice consumatore meno dettagliato quando si converte in tipi di base. One for String ('operatore pubblico implicito statico String (percentuale pct) {return pct.ToString();}) e altro per Decimal, poiché ho modificato il campione originale per l'utilizzo del decimale per una precisione migliore (' operatore implicito pubblico statico Decimale (percentuale pct) {return pct._value;} '). –

4

riflettendo in .NET 4, qui è l'implementazione Microsoft (trovato in System.Windows.Documents.ZoomPercentageConverter.ConvertBack). È possibile modificare questo in base alle proprie esigenze. Io uso sempre l'implementazione di MS, quando possibile!

 try 
     { 
      string str = (string) value; 
      if ((culture != null) && !string.IsNullOrEmpty(str)) 
      { 
       str = ((string) value).Trim(); 
       if ((!culture.IsNeutralCulture && (str.Length > 0)) && (culture.NumberFormat != null)) 
       { 
        switch (culture.NumberFormat.PercentPositivePattern) 
        { 
         case 0: 
         case 1: 
          if ((str.Length - 1) == str.LastIndexOf(culture.NumberFormat.PercentSymbol, StringComparison.CurrentCultureIgnoreCase)) 
          { 
           str = str.Substring(0, str.Length - 1); 
          } 
          break; 

         case 2: 
          if (str.IndexOf(culture.NumberFormat.PercentSymbol, StringComparison.CurrentCultureIgnoreCase) == 0) 
          { 
           str = str.Substring(1); 
          } 
          break; 
        } 
       } 
       num = Convert.ToDouble(str, culture); 
       flag = true; 
      } 
     } 
     catch (ArgumentOutOfRangeException) 
     { 
     } 
     catch (ArgumentNullException) 
     { 
     } 
     catch (FormatException) 
     { 
     } 
     catch (OverflowException) 
     { 
     } 
+0

+1 per menzionare "ZoomPercentageConverter" di WPF. Ciò aiuterà in modo significativo le persone che vengono in questa sessione di domande e risposte in uno scenario WPF, in cui possono semplicemente utilizzare il convertitore incorporato senza scrivere alcun nuovo codice. –

5

È anche possibile combinare le due risposte principali per evitare di accettare valori non validi pur mantenendo flessibile per culture diverse.

var num = double.Parse(value.TrimEnd(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol.ToCharArray()))/100d; 
5

Sembra che molte risposte a questa domanda comportano la sostituzione simbolo percentuale della coltura con la stringa vuota, e quindi parsing della stringa risultante come valore numerico.

Forse mi manca qualcosa, ma qui ci sono ancora alcuni casi non gestiti. In particolare, cosa succede se lo PercentDecimalSeparator è diverso da NumberDecimalSeparator per la cultura corrente? Cosa succede se lo PercentGroupSeparator è diverso da NumberGroupSeparator per la cultura corrente? Cosa succede se lo PercentGroupSizes è diverso da NumberGroupSizes?

Indipendentemente dal fatto che tale cultura esista praticamente (se così non fosse, potrebbe benissimo esistere in futuro se la formattazione di una cultura è cambiata), penso che si possa trovare una soluzione migliore al problema se consideriamo questi casi aggiuntivi e speciali.

Ecco un frammento di codice che mostra una situazione in cui le altre risposte (solo sulla base di sostituire il simbolo di percentuale) avrà esito negativo, e un suggerimento per come potrebbe essere fatto meglio correttamente:

 // Modify a culture so that it has different decimal separators and group separators for numbers and percentages. 
     var customCulture = new CultureInfo("en-US") 
      { 
       NumberFormat = { PercentDecimalSeparator = "PDS", NumberDecimalSeparator = "NDS", PercentGroupSeparator = "PGS", NumberGroupSeparator = "NGS", PercentSymbol = "PS"} 
      }; 
     // Set the current thread's culture to our custom culture 
     Thread.CurrentThread.CurrentCulture = customCulture; 
     // Create a percentage format string from a decimal value 
     var percentStringCustomCulture = 123.45m.ToString("p"); 
     Console.WriteLine(percentStringCustomCulture); // renders "12PGS345PDS00 PS" 
     // Now just replace the percent symbol only, and try to parse as a numeric value (as suggested in the other answers) 
     var deceptiveNumericStringInCustomCulture = percentStringCustomCulture.Replace(customCulture.NumberFormat.PercentSymbol, string.Empty); 
     // THE FOLLOWING LINE THROWS A FORMATEXCEPTION 
     var decimalParsedFromDeceptiveNumericStringInCustomCulture = decimal.Parse(deceptiveNumericStringInCustomCulture); 

     // A better solution...replace the decimal separators and number group separators as well. 
     var betterNumericStringInCustomCulture = deceptiveNumericStringInCustomCulture.Replace(customCulture.NumberFormat.PercentDecimalSeparator, customCulture.NumberFormat.NumberDecimalSeparator); 
     // Here we mitigates issues potentially caused by group sizes by replacing the group separator by the empty string 
     betterNumericStringInCustomCulture = betterNumericStringInCustomCulture.Replace(customCulture.NumberFormat.PercentGroupSeparator, string.Empty); 
     // The following parse then yields the correct result 
     var decimalParsedFromBetterNumericStringInCustomCulture = decimal.Parse(betterNumericStringInCustomCulture)/100m; 

Sì, il codice è un po 'più lungo, e forse sono pedante (cioè forse una tale cultura non esisterà mai realmente). Detto questo, mi sembra una soluzione più generale. Spero che aiuti qualcuno :)

+0

Ottimo lavoro! potrebbe aiutarmi molto Nel frattempo sono sorpreso che MS si sia dimostrato decimale. ToString ("P2"); Perché non forniscono decimal.ParseExact ("P2", stringValue). –

+0

Questo è il migliore se si desidera prendere in considerazione diverse culture. Nota: cosa significa "10 %%"? Secondo il tuo codice è uguale a 0,1, ma si potrebbe anche dire che significa (10%)%, che equivale a 0,001. Suggerimento: rimuovere solo il primo segno di percentuale e chiamare la funzione in modo ricorsivo. Tuttavia, questo non tiene conto del PercentPositivePattern e del PercentNegativePattern, quindi se hai un numero con un mix di pattern percentuali di culture diverse, generi ancora un numero come se fosse valido: "% -10%" –

1

Non sono sicuro di cosa si tratti con questa sostituzione di stringhe, sostituzioni e convertitori.

Utilizzare la porzione Valuta numerica, ma riempirla con i formati percentuali della cultura richiesta.

// input test value 
 
string value = (.015m).ToString("P", CultureInfo.CurrentCulture); 
 

 
// set up your format. 
 
double doubleTest; 
 
var numFormat = CultureInfo.CurrentCulture.NumberFormat; 
 

 
NumberFormatInfo nfi = new NumberFormatInfo() 
 
{ 
 
    CurrencyDecimalDigits = numFormat.PercentDecimalDigits, 
 
    CurrencyDecimalSeparator = numFormat.PercentDecimalSeparator, 
 
    CurrencyGroupSeparator = numFormat.PercentGroupSeparator, 
 
    CurrencyGroupSizes = numFormat.PercentGroupSizes, 
 
    CurrencyNegativePattern = numFormat.PercentNegativePattern, 
 
    CurrencyPositivePattern = numFormat.PercentPositivePattern, 
 
    CurrencySymbol = numFormat.PercentSymbol 
 
}; 
 

 
// load it. 
 
if (double.TryParse(value, NumberStyles.Currency, nfi, out doubleTest)) 
 
{ 
 
    doubleTest /= 100D; 
 
    // use as required. 
 
}

+1

Devi dividere per 100. –

+0

@ErwinMayer Ho aggiunto la correzione. – midspace

Problemi correlati