views:

35

answers:

4

I am at an internship where there is parsing done on strings read from a XML file. Specifically the strings are representations of decimal numbers. A problem arises when I try to parse a decimal string formatted differently than the ones that have comma separators and a decimal point. For example the way that nations format their decimal numbers differently:

  • France: 1 234 567,89 == 1,234,567.89
  • Germany: 1.234.567,89 == 1,234,567.89
  • Australia: 1 234 567.89 == 1,234,567,89

I'm pretty sure that's how those countries can represent decimal numbers. If not sorry. Point is 1,234,567.89 may be represented many ways.

What I would like to do is ensure that whatever string representation of a decimal number I try to parse it ought to come out 1,234,567.89.

I thought that a good way to go about this would be to use the double.TryParse() method but I have been unable to get it to work.

Here is what I got in just a little test application:

double num;
Console.WriteLine(double.TryParse("1 234 567,89", NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat, out num).ToString());
Console.WriteLine(num.ToString());
Console.WriteLine(double.TryParse("1.234.567,89", NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat, out num).ToString());
Console.WriteLine(num.ToString());
Console.WriteLine(double.TryParse("1 234 567.89", NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat, out num).ToString());
Console.WriteLine(num.ToString());

Where all I do is check to see that TryParse worked and then print the number. In this case TryParse always outputs false. The false means that TryParse caught a FormatException and it obviously failed converting the string to a double.

Does this look right or am I just completely confused about what I am doing?

I'm under the impression that by saying NumberStyles.Any it indicates that the string can be in any form of a decimal number. I am also under the impression that saying CultureInfo.InvariantCulture.NumberFormat returns the formatting information of numbers that are culturally independent. In other words it will create a decimal of the form 1,234,567.89.

Thanks for taking the time to read my problem. Any help would be much appreciated.

A: 

Different culture have different thousands separators, decimal separators and more, so you need to use the appropriate CultureInfo for the culture.

You are using InvariantCulture for all of them - it stands for no culture at all and by default, the specific number separators are the same as the ones for en-US.

For example, if you want to parse a number that is in a French format, example taken from MSDN (slightly modified):

double number;
string value = "1345,978";
NumberStyle style = NumberStyles.AllowDecimalPoint;
CultureInfo culture = CultureInfo.CreateSpecificCulture("fr-FR");
if (Double.TryParse(value, style, culture, out number))
   Console.WriteLine("Converted '{0}' to {1}.", value, number);

Displays:

Converted '1345,978' to 1345.978.

Oded
I guess I used CultureInfo.InvariantCulture wrong as I thought it was what it was going to be converted too. Not what the culture the string was from. Is there a "All Cultures"? I didn't see anything in MSDN that jumped out to me.
Chris
@Chris - no, no such thing. One of the things that define a culture is specific numeric formatting (which includes thousands separators, decimal separators etc).
Oded
A: 

If you're always guaranteed to have the cents 1,234,567.00, you can ignore all the punctuation

Beth
A: 

I would suggest that you all agree on CultureInfo.InvariantCulture.NumberFormat. You may try some formal specification like XSD that can be validated automatically.

Albin Sunnanbo
A: 

This does the job in any scenario. Its a little bit parsing.

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);
}
JanW