将字符串通用解析为浮点数

5
在我要运行程序的环境下,人们在使用带有','和'.'分隔符的PC时随机使用逗号和句点作为小数分隔符。如何实现floatparse(string)函数呢?我尝试了以下代码:
    try
    {
        d = float.Parse(s);
    }
    catch
    {
        try
        {
            d = float.Parse(s.Replace(".", ","));
        }
        catch
        {
            d = float.Parse(s.Replace(",", "."));
        }
    }

它不能工作。当我调试时,发现它第一次解析时错误地认为“.”是千位分隔符(例如100.000.000,0)。

我对C#不熟悉,希望有比这更简单的解决方案 :-)

NB:在具有不同分隔符设置的PC上,人们将使用“。”和“,”。


当有人写下100.000.00时,您希望发生什么?您希望它被视为10000.00还是100.0000?或者它不是一个数字? - Filip Ekberg
嗯,多个非数字字符都不管它! - Vladimir Keleshev
一种更灵活的解决方案可能是允许用户选择他们的文化。这样,所有数字和日期都可以自动格式化为他们习惯的方式。ToString() 和 Format() 都可以正常工作,而不需要试图“黑客”它。或者让 IT 修复计算机,以便正确设置文化... - Greg
3个回答

10

如果你确定没有人使用千位分隔符,你可以先进行替换:

string s = "1,23";  // or s = "1.23";

s = s.Replace(',', '.');
double d = double.Parse(s, CultureInfo.InvariantCulture);

一般模式如下:

  • 首先尝试进行清理和标准化,例如使用Replace(otherChar, myChar)。
  • 尝试检测格式,可能使用正则表达式,并使用有针对性的转换。例如通过计算.和,来猜测是否使用千位分隔符。
  • 按顺序尝试多种格式,使用TryParse。不要使用异常。

据我所知,在不变文化中,分隔符始终为“.”? - Vladimir Keleshev
嗯,VS2008EE说“CultureInfo”在当前上下文中不存在...我需要以某种方式声明它吗? - Vladimir Keleshev
你需要导入 System.Globalization。 - Filip Ekberg
谢谢,可以用!您知道在程序中全局设置InvariantCulture是否可行吗? - Vladimir Keleshev
你不能为线程设置InvariantCulture。你需要将其作为IFormatProvider参数传递到Single.TryParse()方法中。 - codekaizen
@Halst:不变量只是为此方便的一种选择,但您也可以选择另一个固定文化。 - H H

2
解析使用 CultureInfo.CurrentCulture 的设置,该设置反映了用户通过区域设置选择的语言和数字格式。如果您的用户键入与他们选择的计算机语言对应的小数,则使用普通的 double.Parse() 应该没有问题。如果用户将其区域设置设置为希腊语并键入 "8,5",double.Parse("8,5") 将返回 8.5。如果他键入 "8.5",parse 将返回 85。
如果用户将其区域设置设置为一种方式,然后开始使用错误的小数,您将面临问题。没有干净的方法来区分这样的错误输入和真正想要输入分组字符的输入。您可以在数字太短以包含分组字符时警告用户,并使用掩码或数字文本框防止错误输入。
另一个有些严格的选项是,在您的应用程序中取消数字输入的分组字符,以在文本框的 KeyDown 事件中取消它。您可以从 CultureInfo.CurrentCulture.NumberFormat 属性中获取数字和分组字符。
尝试替换小数点和分组字符注定会失败,因为它取决于在编译时知道用户将使用什么样的分隔符。如果您知道这一点,您可以使用用户心中的CultureInfo解析数字。不幸的是,User.Brain.CultureInfo 还没有成为.NET框架的一部分 :P

1

我会做类似于这样的事情

float ConvertToFloat(string value)
{
    float result;

    var converted  = float.TryParse(value, out result);

    if (converted) return result;

    converted = float.TryParse(value.Replace(".", ",")), 
                               out result);

    if (converted) return result;

    return float.NaN;
}

在这种情况下,以下内容将返回正确的数据。
        Console.WriteLine(ConvertToFloat("10.10").ToString());
        Console.WriteLine(ConvertToFloat("11,0").ToString());
        Console.WriteLine(ConvertToFloat("12").ToString());
        Console.WriteLine(ConvertToFloat("1 . 10").ToString());

返回

10,1
11
12
NaN

在这种情况下,如果无法进行转换,您至少会知道它不是一个数字。这是一种安全的转换方式。
您还可以使用以下重载。
float.TryParse(value,
            NumberStyles.Currency,
            CultureInfo.CurrentCulture,
            out result)

在这个测试代码中:

Console.WriteLine(ConvertToFloat("10,10").ToString());
Console.WriteLine(ConvertToFloat("11,0").ToString());
Console.WriteLine(ConvertToFloat("12").ToString());
Console.WriteLine(ConvertToFloat("1 . 10").ToString());
Console.WriteLine(ConvertToFloat("100.000,1").ToString());

它返回以下内容

10,1
11
12
110
100000,1

所以,根据您想对用户有多“友好”,如果最后一步不是数字,您可以始终尝试以这种方式转换它,否则它确实不是一个数字。

它看起来会像这样

float ConvertToFloat(string value)
{
    float result;

    var converted = float.TryParse(value,
                                    out result);


    if (converted) return result;

    converted = float.TryParse(value.Replace(".", ","),
                                out result);

    if (converted) return result;

    converted = float.TryParse(value,
                                    NumberStyles.Currency,
                                    CultureInfo.CurrentCulture,
                                    out result);

    return converted ? result : float.NaN;
}

以下是哪里

Console.WriteLine(ConvertToFloat("10,10").ToString());
Console.WriteLine(ConvertToFloat("11,0").ToString());
Console.WriteLine(ConvertToFloat("12").ToString());
Console.WriteLine(ConvertToFloat("1 . 10").ToString());
Console.WriteLine(ConvertToFloat("100.000,1").ToString());
Console.WriteLine(ConvertToFloat("asdf").ToString());

返回

10,1
11
12
110
100000,1
NaN

在欧洲格式的PC上,“var converted”将始终为真,因为它忽略了“.”,因为它认为“.”是千位分隔符(100.000.000,0)。 - Vladimir Keleshev
我的电脑是欧洲版的,100.000.000,0 根本不起作用。您可以设置第二个参数 NumberStyles 和 CultureInfo。 - Filip Ekberg
好的,也许这只是与我的丹麦电脑'文化'相关的特殊问题,但当输入为123.456时,[var converted = float.TryParse(value, out result);]会返回123456并返回true。 - Vladimir Keleshev
您可以设置两个额外的参数来解决这个问题。 - Filip Ekberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接