如何使用正确的CultureInfo将字符串转换为双精度型数据

42

我有一个数据库中的两个nvarchar字段来存储数据类型和默认值,我有一个数据类型Double和值为65.89875(英文格式)。

现在我希望用户按照所选的浏览器语言格式查看该值(英文中的65.89875应以德文格式显示为65,89875)。现在,如果用户从德文格式编辑为65,89875,这相当于英文中的65.89875,而另一个用户从英文浏览器查看,则会显示为6589875。

这是因为在数据库中,它被存储为nvarchr列中的65,89875,当使用英文文化进行转换时,它会变成6589875,因为它将“,”视为分隔符,这是德语的小数点操作符。

如何使所有浏览器都能正常工作?


1
你为什么一开始就把数字存储为文本呢?如果可能的话,我强烈建议你更改架构。 - Jon Skeet
7个回答

70

您需要定义一个单一的区域设置,用于存储在数据库中的数据,无文化区是为此目的而存在的。

当您显示时,请将其转换为本地类型,然后按用户的文化格式化。

例如,要显示:

string fromDb = "123.56";
string display = double.Parse(fromDb, CultureInfo.InvariantCulture).ToString(userCulture);

存储:

string fromUser = "132,56";
double value;
// Probably want to use a more specific NumberStyles selection here.
if (!double.TryParse(fromUser, NumberStyles.Any, userCulture, out value)) {
  // Error...
}
string forDB = value.ToString(CultureInfo.InvariantCulture);

PS.几乎不用说,使用与数据匹配的数据类型的列会更好(但有时可能存在遗留问题)。


double.TryParse()甚至没有区域设置的参数... 这个答案并没有真正帮助我 ;/ - slow
2
@sLw CultureInfo 实现了 IFormatProvider: 该重载 允许您传递所需的区域设置。但是 - 五年来 - 这个函数一直存在第二个和第三个参数颠倒的问题(已经修正)! - Richard

6

您可以将UI文化更改为任何您想要的内容,但是您应该像这样更改数字分隔符:

CultureInfo info = new CultureInfo("fa-IR");
info.NumberFormat.NumberDecimalSeparator = ".";
Thread.CurrentThread.CurrentCulture = info;
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;

通过这个方法,你的字符串将会被转换为这种格式:"12.49" 而不是 "12,49" 或者 "12/49"


4

Convert.ToDouble(x)函数也可以有第二个参数,用于指定CultureInfo。当你将其设置为System.Globalization.CultureInfo.InvariantCulture时,结果将始终相同。


3

我从MSDN上得到了一些帮助,但这是我的答案:

double number;
string localStringNumber;
string doubleNumericValueasString = "65.89875";
System.Globalization.NumberStyles style = System.Globalization.NumberStyles.AllowDecimalPoint;

if (double.TryParse(doubleNumericValueasString, style, System.Globalization.CultureInfo.InvariantCulture, out number))
    Console.WriteLine("Converted '{0}' to {1}.", doubleNumericValueasString, number);
else
    Console.WriteLine("Unable to convert '{0}'.", doubleNumericValueasString);
localStringNumber =number.ToString(System.Globalization.CultureInfo.CreateSpecificCulture("de-DE"));

2

您可以使用FormatProviders将用户提供的值转换为double,并再次存储为nvarchar。 CultureInfo是一个典型的FormatProvider。假设您知道您正在操作的文化,

System.Globalization.CultureInfo EnglishCulture = new System.Globalization.CultureInfo("en-EN");
System.Globalization.CultureInfo GermanCulture = new System.Globalization.CultureInfo("de-de");

会做必要的转换,例如:;
double val;
if(double.TryParse("65,89875", System.Globalization.NumberStyles.Float, GermanCulture,  out val))
{
    string valInGermanFormat = val.ToString(GermanCulture);
    string valInEnglishFormat = val.ToString(EnglishCulture);
}

if(double.TryParse("65.89875", System.Globalization.NumberStyles.Float, EnglishCulture,  out val))
{
    string valInGermanFormat = val.ToString(GermanCulture);
    string valInEnglishFormat = val.ToString(EnglishCulture);
}

@Richard,我已经找过了,但没有找到,谢谢。 - tafa

1
使用InvariantCulture。小数点始终是“。”最终可以将“,”替换为“。”当您显示结果时,请使用本地文化。但在内部始终使用不变的文化。
TryParse并不总是按我们所期望的方式工作。在这个领域中,.net有变更请求。

https://github.com/dotnet/runtime/issues/25868


0

我在我的工具箱中有这个函数已经好几年了(所有的函数和变量名称都很混乱,混合了西班牙语和英语,对此我感到抱歉)。

它允许用户使用来分隔小数,并尽可能地尝试处理这两个符号。

    Public Shared Function TryCDec(ByVal texto As String, Optional ByVal DefaultValue As Decimal = 0) As Decimal

        If String.IsNullOrEmpty(texto) Then
            Return DefaultValue
        End If

        Dim CurAsTexto As String = texto.Trim.Replace("$", "").Replace(" ", "")

        ''// You can probably use a more modern way to find out the
        ''// System current locale, this function was done long time ago
        Dim SepDecimal As String, SepMiles As String
        If CDbl("3,24") = 324 Then
            SepDecimal = "."
            SepMiles = ","
        Else
            SepDecimal = ","
            SepMiles = "."
        End If

        If InStr(CurAsTexto, SepDecimal) > 0 Then
            If InStr(CurAsTexto, SepMiles) > 0 Then
                ''//both symbols was used find out what was correct
                If InStr(CurAsTexto, SepDecimal) > InStr(CurAsTexto, SepMiles) Then
                    ''// The usage was correct, but get rid of thousand separator
                    CurAsTexto = Replace(CurAsTexto, SepMiles, "")
                Else
                    ''// The usage was incorrect, but get rid of decimal separator and then replace it
                    CurAsTexto = Replace(CurAsTexto, SepDecimal, "")
                    CurAsTexto = Replace(CurAsTexto, SepMiles, SepDecimal)
                End If
            End If
        Else
            CurAsTexto = Replace(CurAsTexto, SepMiles, SepDecimal)
        End If
        ''// At last we try to tryParse, just in case
        Dim retval As Decimal = DefaultValue
        Decimal.TryParse(CurAsTexto, retval)
        Return retval
    End Function

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