9

I need a function to parse a user inputs of numbers to doubles. I cannot do anything client side or change how the input comes in.

Input       | Desired Output
"9"         | 9
"9 3/4"     | 9.75
" 9  1/ 2 " | 9.5
"9 .25"     | 9.25
"9,000 1/3" | 9000.33
"1/4"       | .25

I saw this post, but it uses Python, I was just wondering if anybody knew any fancy C# ways of handling this before I spend time to write my own.

Community
  • 1
  • 1
Greg
  • 8,574
  • 21
  • 67
  • 109
  • Have you seen [this](http://stackoverflow.com/questions/7564906/convert-double-to-fraction-as-string-in-c-sharp) question? – Bernard Jan 06 '12 at 17:08

8 Answers8

5

I would use regular expressions for this one:

Regex re = new Regex(@"^\s*(\d+)(\s*\.(\d*)|\s+(\d+)\s*/\s*(\d+))?\s*$");
string str = " 9  1/ 2 ";
Match m = re.Match(str);
double val = m.Groups[1].Success ? double.Parse(m.Groups[1].Value) : 0.0;

if(m.Groups[3].Success) {
    val += double.Parse("0." + m.Groups[3].Value);
} else {
    val += double.Parse(m.Groups[4].Value) / double.Parse(m.Groups[5].Value);
}

Untested, as of yet, but I think it should work.

Here's a demo, and here's another demo.

Ry-
  • 218,210
  • 55
  • 464
  • 476
3

There is nothing built in the BCL that will do this, but there are plenty of existing mathematical expression parsers that will (though this may be over the top for this specific situation).

Writing one yourself, for the limited use cases you have posted shouldn't be difficult.

Oded
  • 489,969
  • 99
  • 883
  • 1,009
3

I see two sections. Everything before the first space is the integral section. Everything after the first space is the fractional section. After you separate the two sections, you can just strip spaces from the fractional section, split that section on the / character, and divide the first part by the 2nd part (if there is a 2nd part). Then add the result to the integral section to find your answer.

This algorithm should give a correct result for each of your samples. It might also give an incorrect result for samples like these: "9 .25/4" or "9 3/0", so those are things to watch for. Other things include leading whitespace, whether you want to allow other whitespace, currency symbols, whether "9.25" (no spaces) is a valid input, and what to do with irrational fractions like "1/3", "1/10" (irrational in binary), etc.

I'm not normally a huge believer in test driven design (that you should write the tests first and go for 100% coverage) for static-typed languages, but I do think unit tests have value in certain specific situations, and this is one of those situations. I would put together a few tests for both some common and edge cases, such that you can be sure whatever you end up with handles the inputs correctly to pass the tests.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
2

Here's what I ended up using:

private double ParseDoubleFromString(string num)
{
    //removes multiple spces between characters, cammas, and leading/trailing whitespace
    num = Regex.Replace(num.Replace(",", ""), @"\s+", " ").Trim();
    double d = 0;
    int whole = 0;
    double numerator;
    double denominator;

    //is there a fraction?
    if (num.Contains("/"))
    {
        //is there a space?
        if (num.Contains(" "))
        {
            //seperate the integer and fraction
            int firstspace = num.IndexOf(" ");
            string fraction = num.Substring(firstspace, num.Length - firstspace);
            //set the integer
            whole = int.Parse(num.Substring(0, firstspace));
            //set the numerator and denominator
            numerator = double.Parse(fraction.Split("/".ToCharArray())[0]);
            denominator = double.Parse(fraction.Split("/".ToCharArray())[1]);
        }
        else
        {
            //set the numerator and denominator
            numerator = double.Parse(num.Split("/".ToCharArray())[0]);
            denominator = double.Parse(num.Split("/".ToCharArray())[1]);
        }

        //is it a valid fraction?
        if (denominator != 0)
        {
            d = whole + (numerator / denominator);
        }
    }
    else
    {
        //parse the whole thing
        d = double.Parse(num.Replace(" ", ""));
    }

    return d;
}
Greg
  • 8,574
  • 21
  • 67
  • 109
1

It doesn't look very difficult to write some code that would do this. First try removing all spaces and see if it's a legal number. If it's not, find the legal numbers (such as 9, 3, 4 in "9 3/4" and do a simple arithmetic operation: 9 + 3 / 4 = 9.75

Ilya Kogan
  • 21,995
  • 15
  • 85
  • 141
  • 1
    If you remove all spaces, "9 3/4" becomes "93/4". – phoog Jan 06 '12 at 17:13
  • I know. The removal of all spaces is only for the first attempt (trying to parse "9 .25" as "9.25"). In order to parse "9 3/4" you have to leave the spaces. – Ilya Kogan Jan 06 '12 at 17:15
0

Solution below won't work for negative fractions. Can be improved by changing

    //is it a valid fraction?
    if (denominator != 0)
    {
        d = whole + (numerator / denominator);
    }
    to
    //is it a valid fraction?
    if (denominator != .0)
    {
        var sign = Math.Sign(whole);

        d = whole + sign*(numerator/denominator);
    }
user2284570
  • 2,891
  • 3
  • 26
  • 74
YevT
  • 1
  • 1
0

I wrote this method for this work:

private double DoWork(string data)
    {
        double final = 0;

        foreach (string s in data.Split(' '))
        {
            if (s.Contains('/'))
            {
                final += double.Parse(s.Split('/')[0]) / double.Parse(s.Split('/')[1]);
            }
            else
            {
                double tryparse = 0;
                double.TryParse(s, out tryparse);
                final += tryparse;
            }
        }

        return final;
    }
ahmadali shafiee
  • 4,350
  • 12
  • 56
  • 91
0

Is it useful to you ?

I think you can also use the dynamically compiling code

    static void Main(string[] args)
    {
        var value = "9 3/4";
        value = value.Split(' ')[0] + "d + " + value.Split(' ')[1] + "d";

        var exp = " public class DynamicComputer { public static double Eval() { return " + value + "; }}";

        CodeDomProvider cp = new Microsoft.CSharp.CSharpCodeProvider();
        ICodeCompiler icc = cp.CreateCompiler();
        CompilerParameters cps = new CompilerParameters();
        CompilerResults cres;

        cps.GenerateInMemory = true;

        cres = icc.CompileAssemblyFromSource(cps, exp);

        Assembly asm = cres.CompiledAssembly;

        Type t = asm.GetType("DynamicComputer");

        double d = (double)t.InvokeMember("Eval",
            BindingFlags.InvokeMethod,
            null,
            null,
            null);

        Console.WriteLine(d);

        Console.Read();
    }
shenhengbin
  • 4,236
  • 1
  • 24
  • 33