如何将百分比字符串转换为双倍?

sas*_*eve 33 c# formatting

我有一个像"1.5%"的字符串,并希望将其转换为double值.

可以通过以下方式简单地完成:

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

但我不想使用这种解析方法.

有没有其他方法与IFormatProvider或类似的东西?

Han*_*ant 47

它是文化敏感的,取而代之的是:

  value = value.Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol, "");
Run Code Online (Sandbox Code Playgroud)

然后解析它.


tva*_*son 46

如果你关心捕捉格式错误,我会使用TrimEnd而不是Replace.替换将允许格式错误未被检测到.

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

这将确保该值必须是一些十进制数后跟任意数量的空格和百分号,即它必须至少以正确格式的值开头.更准确地说,您可能希望拆分'%',而不是删除空条目,然后确保只有两个结果,第二个是空的.第一个应该是要转换的值.

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

使用Replace将允许您成功地,错误地IMO,解析如下内容:

  • 1.5%
  • 1%0.5
  • 1. 5%

除此之外 1.5%

  • 有些文化以百分比开头,所以你真的需要CultureInfo.CurrentCulture.NumberFormat (3认同)

Nic*_*ver 9

只是略好一点,但不容易出错:

public static double FromPercentageString(this string value)
{
    return double.Parse(value.Replace("%","")) / 100;
}
Run Code Online (Sandbox Code Playgroud)


Lac*_*che 8

TypeConverter提供了将值类型转换为其他类型以及访问标准值和子属性的统一方法.http://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter%28VS.80%29.aspx

对于一次性转换,这可能有点过分.在ASP.NET或XAML中绑定属性或解析配置文件时,它更有用.

var result = new Percentage("1.5%");
double d = result.Value;
Run Code Online (Sandbox Code Playgroud)

百分比及其TypeConverter定义为:

[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 );
    }
}
Run Code Online (Sandbox Code Playgroud)

  • +1.恕我直言,这是这个问题的最佳答案.除了代码之外,我还会在转换为基类型时为较不详细的使用者代码添加两个隐式转换运算符.一个用于String(`静态公共隐式运算符String(Percentage pct){return pct.ToString();}`)和其他用于Decimal,因为我已经更改了原始样本以使用十进制来获得更好的精度(`static public implicit operator十进制(百分比pct){return pct._value;}`). (4认同)

Rob*_*eer 6

您还可以将前两个答案组合在一起,以避免接受无效值,同时保持其对不同文化的灵活性.

var num = double.Parse(value.TrimEnd(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol.ToCharArray() ) ) / 100d;
Run Code Online (Sandbox Code Playgroud)


sam*_*y34 6

似乎这个问题的许多答案涉及用空字符串替换文化的百分比符号,然后将结果字符串解析为数值.

也许我错过了什么,但这里还有一些未处理的案例.具体来说,如果与当前文化PercentDecimalSeparator不同,会发生什么NumberDecimalSeparator?如果与当前文化PercentGroupSeparator不同,会发生什么NumberGroupSeparator?如果PercentGroupSizesNumberGroupSizes?不同会发生什么?

无论这种文化是否实际存在(如果不存在,如果文化的格式发生变化,将来很可能会存在),我认为如果我们考虑这些文化,可以找到更好的解决方案.额外的特殊情况.

这是一个代码片段,显示其他答案(仅基于替换百分号)将失败的情况,并建议如何更好地完成它:

        // 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;
Run Code Online (Sandbox Code Playgroud)

是的,代码有点长,也许我是迂腐的(也许这样的文化永远不会存在).也就是说,在我看来,这是一个更通用的解决方案.希望它能帮到某人:).


mid*_*ace 5

我不确定所有这些字符串替换、替换和转换器是什么。

使用 NumberFormat Currency 部分,但使用您所需区域性的百分比格式填充它。

// 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.
}
Run Code Online (Sandbox Code Playgroud)