universal parsing of strings into float
In the environment that my program is going to run, people use ',' and '.' as decimal separators randomly on PCs with ',' and '.' separators.
How would you implements such a floatparse(string) function?
I tried this one:
try
{
d = float.Parse(s);
}
catch
{
try
{
d = float.Parse(s.Replace(".", ","));
}
catch
{
开发者_开发百科 d = float.Parse(s.Replace(",", "."));
}
}
It doesn't work. And when I debugg it turns out that it parses it wrong the first time thinking that "." is a separator for thousands (like 100.000.000,0).
I'm noob at C#, so hopefully there is less overcomplicated solution then that :-)
NB: People a going to use both '.' and ',' in PCs with different separator settings.
If you are sure nobody uses thousand-separators, you can Replace first:
string s = "1,23"; // or s = "1.23";
s = s.Replace(',', '.');
double d = double.Parse(s, CultureInfo.InvariantCulture);
The general pattern would be:
- first try to sanitize and normalize. Like Replace(otherChar, myChar).
- try to detect a format, maybe using RegEx, and use a targeted conversion. Like counting . and , to guess whether thousand separators are used.
- try several formats in order, with TryParse. Do not use exceptions for this.
Parsing uses the settings of the CultureInfo.CurrentCulture, which reflects the language and the Number format selected by the user through Regional Settings. If your users type the decimals that correspond to the language they chose for their computers, you should have no problem using plain old double.Parse(). If a user sets his locale to Greek and types "8,5", double.Parse("8,5") will return 8.5. If he types "8.5" parse will return 85.
If a user sets his locale to one setting and then starts using the wrong decimal, you face a problem. There is no clean way to separate such wrong entries instead of entries that really wanted to enter the grouping character. What you can do is to warn the user when a number is too short to include a grouping character and use Masked or numerical text boxes to prevent wrong entries.
Another, somewhat stricter option, is to disallow the grouping character for number entry in your application by cancelling it in the KeyDown event of your textboxes. You can get the numeric and grouping characters from CultureInfo.CurrentCulture.NumberFormat property.
Trying to replace the decimal and grouping characters is doomed to fail as it depends on knowing during compile time what kind of separator the user is going to use. If you knew that, you could just parse the number using the CultureInfo in the user's mind. Unfortunately, User.Brain.CultureInfo is not yet part of the .NET framework :P
I would do someting like this
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;
}
In this case the following would return correct data
Console.WriteLine(ConvertToFloat("10.10").ToString());
Console.WriteLine(ConvertToFloat("11,0").ToString());
Console.WriteLine(ConvertToFloat("12").ToString());
Console.WriteLine(ConvertToFloat("1 . 10").ToString());
Returns
10,1
11
12
NaN
In this case if it is not possible to convert it, you will at least know that it is not a number. It's a safe way to convert.
You can also use the following overload
float.TryParse(value,
NumberStyles.Currency,
CultureInfo.CurrentCulture,
out result)
On this test-code:
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());
It returns the following
10,1
11
12
110
100000,1
So depending on how "nice" you want to be to the user, you can always replace the last step, if it is not a number, try converting it this way aswell, otherwsie it really isn't a number.
It would the look like this
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;
}
Where the following
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());
Returns
10,1
11
12
110
100000,1
NaN
精彩评论