我如何解析一个字符串与小数点为双?

我想把"3.5"这样的字符串解析为double类型。然而,

double.Parse("3.5")

收益率为35和

double.Parse("3.5", System.Globalization.NumberStyles.AllowDecimalPoint)

抛出FormatException

现在我的计算机的区域设置为德语,其中逗号用作小数分隔符。它可能必须做一些事情,并且double.Parse()期望"3,5"作为输入,但我不确定。

如何解析包含十进制数字的字符串,该十进制数字可能是或可能不是我当前地区指定的格式?

411786 次浏览
double.Parse("3.5", CultureInfo.InvariantCulture)

诀窍是使用不变区域性,来解析所有区域性中的dot。

double.Parse("3.5", System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.NumberFormatInfo.InvariantInfo);

我通常使用多区域性函数来解析用户输入,主要是因为如果某人习惯使用numpad,并且正在使用使用逗号作为小数分隔符的区域性,那么此人将使用numpad的点而不是逗号。

public static double GetDouble(string value, double defaultValue)
{
double result;


//Try parsing in the current culture
if (!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, out result) &&
//Then try in US english
!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.GetCultureInfo("en-US"), out result) &&
//Then in neutral language
!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out result))
{
result = defaultValue;
}


return result;
}

不过要注意,@nikie的评论是真的。我要为自己辩护的是,我在受控环境中使用这个函数,我知道文化可以是en-US、en-CA或fr-CA。我用这个函数是因为在法语中,我们用逗号作为小数分隔符,但任何在金融领域工作过的人都会在numpad上使用小数分隔符,但这是一个点,而不是一个逗号。因此,即使在fr-CA文化中,我也需要解析将以点作为小数分隔符的数字。

Double.Parse("3,5".Replace(',', '.'), CultureInfo.InvariantCulture)

在解析前将逗号替换为一个点。适用于以逗号作为小数分隔符的国家。考虑将用户输入限制为一个逗号或点(如果有必要)。

下面的代码在任何场景下都可以完成这项工作。这是一点解析。

List<string> inputs = new List<string>()
{
"1.234.567,89",
"1 234 567,89",
"1 234 567.89",
"1,234,567.89",
"123456789",
"1234567,89",
"1234567.89",
};
string output;


foreach (string input in inputs)
{
// Unify string (no spaces, only .)
output = input.Trim().Replace(" ", "").Replace(",", ".");


// Split it on points
string[] split = output.Split('.');


if (split.Count() > 1)
{
// Take all parts except last
output = string.Join("", split.Take(split.Count()-1).ToArray());


// Combine token parts with last part
output = string.Format("{0}.{1}", output, split.Last());
}


// Parse double invariant
double d = double.Parse(output, CultureInfo.InvariantCulture);
Console.WriteLine(d);
}

如果数值来自用户输入,我认为100%正确的转换是不可能的。例如,如果值是123.456,它可以是一个分组,也可以是一个小数点。如果你真的需要100%,你必须描述你的格式,如果不正确就抛出异常。

但我改进了JanW的代码,所以我们提前了一点,达到100%。背后的思想是,如果最后一个分隔符是groupseparator,它将更像是一个整数类型,而不是double类型。

添加的代码位于的第一个如果中。

void Main()
{
List<string> inputs = new List<string>() {
"1.234.567,89",
"1 234 567,89",
"1 234 567.89",
"1,234,567.89",
"1234567,89",
"1234567.89",
"123456789",
"123.456.789",
"123,456,789,"
};


foreach(string input in inputs) {
Console.WriteLine(GetDouble(input,0d));
}


}


public static double GetDouble(string value, double defaultValue) {
double result;
string output;


// Check if last seperator==groupSeperator
string groupSep = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator;
if (value.LastIndexOf(groupSep) + 4 == value.Count())
{
bool tryParse = double.TryParse(value, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.CurrentCulture, out result);
result = tryParse ? result : defaultValue;
}
else
{
// Unify string (no spaces, only . )
output = value.Trim().Replace(" ", string.Empty).Replace(",", ".");


// Split it on points
string[] split = output.Split('.');


if (split.Count() > 1)
{
// Take all parts except last
output = string.Join(string.Empty, split.Take(split.Count()-1).ToArray());


// Combine token parts with last part
output = string.Format("{0}.{1}", output, split.Last());
}
// Parse double invariant
result = double.Parse(output, System.Globalization.CultureInfo.InvariantCulture);
}
return result;
}

我也改进了@JanW的代码…

我需要它来格式化医疗器械的结果,他们还发送了“>1000”,“23.3e02”,“350E-02”和“NEGATIVE”。

private string FormatResult(string vResult)
{
string output;
string input = vResult;


// Unify string (no spaces, only .)
output = input.Trim().Replace(" ", "").Replace(",", ".");


// Split it on points
string[] split = output.Split('.');


if (split.Count() > 1)
{
// Take all parts except last
output = string.Join("", split.Take(split.Count() - 1).ToArray());


// Combine token parts with last part
output = string.Format("{0}.{1}", output, split.Last());
}
string sfirst = output.Substring(0, 1);


try
{
if (sfirst == "<" || sfirst == ">")
{
output = output.Replace(sfirst, "");
double res = Double.Parse(output);
return String.Format("{1}{0:0.####}", res, sfirst);
}
else
{
double res = Double.Parse(output);
return String.Format("{0:0.####}", res);
}
}
catch
{
return output;
}
}
string testString1 = "2,457";
string testString2 = "2.457";
double testNum = 0.5;
char decimalSepparator;
decimalSepparator = testNum.ToString()[1];


Console.WriteLine(double.Parse(testString1.Replace('.', decimalSepparator).Replace(',', decimalSepparator)));
Console.WriteLine(double.Parse(testString2.Replace('.', decimalSepparator).Replace(',', decimalSepparator)));

而不是必须在所有解析中指定一个语言环境,我更喜欢设置一个应用程序范围内的语言环境,尽管如果字符串格式在应用程序中不一致,这可能不起作用。

CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("pt-PT");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("pt-PT");

在应用程序的开始定义它将使所有双解析都期望逗号作为小数分隔符。您可以设置适当的区域设置,以便十进制和千位分隔符适合您正在解析的字符串。

看,上面每个建议用常量字符串替换字符串的答案只可能是错误的。为什么?因为你不尊重Windows的区域设置!Windows保证用户可以自由地设置任何分隔符。他/她可以打开控制面板,进入区域面板,点击高级,随时改变角色。甚至在程序运行期间。想想这个。好的解决方案必须意识到这一点。

首先你要问自己,你要解析的这个数字是从哪里来的。如果它来自于。net Framework中的输入没有问题,因为它将是相同的格式。但它可能来自外部,可能来自外部服务器,可能来自只支持字符串属性的旧DB。在这里,db管理员应该已经给出了存储数字的格式规则。例如,如果你知道它将是一个带有US格式的US DB,你可以使用这段代码:

CultureInfo usCulture = new CultureInfo("en-US");
NumberFormatInfo dbNumberFormat = usCulture.NumberFormat;
decimal number = decimal.Parse(db.numberString, dbNumberFormat);

这在世界上任何地方都适用。请不要使用“Convert.ToXxxx”。Convert类只被认为是任意方向转换的基类。此外:您也可以对DateTimes使用类似的机制。

如果没有指定要查找的小数分隔符,这是很困难的,但如果你这样做了,这就是我使用的:

    public static double Parse(string str, char decimalSep)
{
string s = GetInvariantParseString(str, decimalSep);
return double.Parse(s, System.Globalization.CultureInfo.InvariantCulture);
}


public static bool TryParse(string str, char decimalSep, out double result)
{
// NumberStyles.Float | NumberStyles.AllowThousands got from Reflector
return double.TryParse(GetInvariantParseString(str, decimalSep), NumberStyles.Float | NumberStyles.AllowThousands, System.Globalization.CultureInfo.InvariantCulture, out result);
}


private static string GetInvariantParseString(string str, char decimalSep)
{
str = str.Replace(" ", "");


if (decimalSep != '.')
str = SwapChar(str, decimalSep, '.');


return str;
}
public static string SwapChar(string value, char from, char to)
{
if (value == null)
throw new ArgumentNullException("value");


StringBuilder builder = new StringBuilder();


foreach (var item in value)
{
char c = item;
if (c == from)
c = to;
else if (c == to)
c = from;


builder.Append(c);
}
return builder.ToString();
}


private static void ParseTestErr(string p, char p_2)
{
double res;
bool b = TryParse(p, p_2, out res);
if (b)
throw new Exception();
}


private static void ParseTest(double p, string p_2, char p_3)
{
double d = Parse(p_2, p_3);
if (d != p)
throw new Exception();
}


static void Main(string[] args)
{
ParseTest(100100100.100, "100.100.100,100", ',');
ParseTest(100100100.100, "100,100,100.100", '.');
ParseTest(100100100100, "100.100.100.100", ',');
ParseTest(100100100100, "100,100,100,100", '.');
ParseTestErr("100,100,100,100", ',');
ParseTestErr("100.100.100.100", '.');
ParseTest(100100100100, "100 100 100 100.0", '.');
ParseTest(100100100.100, "100 100 100.100", '.');
ParseTest(100100100.100, "100 100 100,100", ',');
ParseTest(100100100100, "100 100 100,100", '.');
ParseTest(1234567.89, "1.234.567,89", ',');
ParseTest(1234567.89, "1 234 567,89", ',');
ParseTest(1234567.89, "1 234 567.89",     '.');
ParseTest(1234567.89, "1,234,567.89",    '.');
ParseTest(1234567.89, "1234567,89",     ',');
ParseTest(1234567.89, "1234567.89",  '.');
ParseTest(123456789, "123456789", '.');
ParseTest(123456789, "123456789", ',');
ParseTest(123456789, "123.456.789", ',');
ParseTest(1234567890, "1.234.567.890", ',');
}

这对任何文化都适用。它正确地无法解析具有多个小数分隔符的字符串,这与replace而不是swap的实现不同。

下面的方法效率较低,但我使用这种逻辑。这只在小数点后有两位数字时有效。

double val;


if (temp.Text.Split('.').Length > 1)
{
val = double.Parse(temp.Text.Split('.')[0]);


if (temp.Text.Split('.')[1].Length == 1)
val += (0.1 * double.Parse(temp.Text.Split('.')[1]));
else
val += (0.01 * double.Parse(temp.Text.Split('.')[1]));
}
else
val = double.Parse(RR(temp.Text));

乘以这个数,然后除以之前乘以的数。

例如,

perc = double.Parse("3.555)*1000;
result = perc/1000

我不能写评论,所以我在这里写:

< em > double.Parse(“3.5”,CultureInfo.InvariantCulture) < / em >不是一个好主意,因为在加拿大我们写3,5而不是3.5,这个函数的结果是35。

我在自己的电脑上进行了测试:

double.Parse("3.5", CultureInfo.InvariantCulture) --> 3.5 OK
double.Parse("3,5", CultureInfo.InvariantCulture) --> 35 not OK

这是< em > Pierre-Alain Vigeant < / em >提到的正确方法

public static double GetDouble(string value, double defaultValue)
{
double result;


// Try parsing in the current culture
if (!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, out result) &&
// Then try in US english
!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.GetCultureInfo("en-US"), out result) &&
// Then in neutral language
!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out result))
{
result = defaultValue;
}
return result;
}
System.Globalization.CultureInfo ci = System.Globalization.CultureInfo.CurrentCulture;


string _pos = dblstr.Replace(".",
ci.NumberFormat.NumberDecimalSeparator).Replace(",",
ci.NumberFormat.NumberDecimalSeparator);


double _dbl = double.Parse(_pos);
        var doublePattern = @"(?<integer>[0-9]+)(?:\,|\.)(?<fraction>[0-9]+)";
var sourceDoubleString = "03444,44426";
var match = Regex.Match(sourceDoubleString, doublePattern);


var doubleResult = match.Success ? double.Parse(match.Groups["integer"].Value) + (match.Groups["fraction"].Value == null ? 0 : double.Parse(match.Groups["fraction"].Value) / Math.Pow(10, match.Groups["fraction"].Value.Length)): 0;
Console.WriteLine("Double of string '{0}' is {1}", sourceDoubleString, doubleResult);

我对这个话题的看法,试图提供一个通用的,双重转换方法:

private static double ParseDouble(object value)
{
double result;


string doubleAsString = value.ToString();
IEnumerable<char> doubleAsCharList = doubleAsString.ToList();


if (doubleAsCharList.Where(ch => ch == '.' || ch == ',').Count() <= 1)
{
double.TryParse(doubleAsString.Replace(',', '.'),
System.Globalization.NumberStyles.Any,
CultureInfo.InvariantCulture,
out result);
}
else
{
if (doubleAsCharList.Where(ch => ch == '.').Count() <= 1
&& doubleAsCharList.Where(ch => ch == ',').Count() > 1)
{
double.TryParse(doubleAsString.Replace(",", string.Empty),
System.Globalization.NumberStyles.Any,
CultureInfo.InvariantCulture,
out result);
}
else if (doubleAsCharList.Where(ch => ch == ',').Count() <= 1
&& doubleAsCharList.Where(ch => ch == '.').Count() > 1)
{
double.TryParse(doubleAsString.Replace(".", string.Empty).Replace(',', '.'),
System.Globalization.NumberStyles.Any,
CultureInfo.InvariantCulture,
out result);
}
else
{
throw new ParsingException($"Error parsing {doubleAsString} as double, try removing thousand separators (if any)");
}
}


return result;
}

与预期工作:

  • 1.1
  • 1, - 1
  • 1000000000年
  • 1.000.000.000
  • 1000000000 .99
  • 1.000.000.000 99
  • 5000111年。3
  • 5.000.111 3
  • 0.99,000,111, 88
  • 0, 99.000.111.88

没有实现默认转换,因此试图解析1.3,141,3.14或类似情况会失败。

这里有一个解决方案,可以处理包含逗号和句点的任何数字字符串。这种解决方案特别适用于金额,因此只需要十分位和百分之一位。任何超过的数都被视为整数。

首先删除不是数字、逗号、句号或负号的任何内容。

string stringAmount = Regex.Replace(originalString, @"[^0-9\.\-,]", "");

然后我们把这个数分成整数和十进制数。

string[] decimalParsed = Regex.Split(stringAmount, @"(?:\.|,)(?=\d{2}$)");

(这个Regex表达式选择一个逗号或句号,它距离字符串的末尾有两个数字。)

现在我们取整个数字,去掉逗号和句号。

string wholeAmount = decimalParsed[0].Replace(",", "").Replace(".", "");


if (wholeAmount.IsNullOrEmpty())
wholeAmount = "0";

现在我们处理小数部分。

string decimalAmount = "00";


if (decimalParsed.Length == 2)
{
decimalAmount = decimalParsed[1];
}

最后,我们可以把整数和小数放在一起并解析Double。

double amount = $"{wholeAmount}.{decimalAmount}".ToDouble();

这将处理200、00、1 000、00、1,000、1.000、33、2,000.000、78等。