views:

116

answers:

2

I want to make a function that converts mixed numbers and fractions (as strings) to floats. Here's some examples:

'1 1/2' -> 1.5
'11/2' -> 5.5
'7/8' -> 0.875
'3' -> 3
'7.7' -> 7.7

I'm currently using this function, but I think it could be improved. It also doesn't handle numbers that are already in decimal representation

def mixedtofloat(txt):

    mixednum = re.compile("(\\d+) (\\d+)\\/(\\d+)",re.IGNORECASE|re.DOTALL)
    fraction = re.compile("(\\d+)\\/(\\d+)",re.IGNORECASE|re.DOTALL)
    integer = re.compile("(\\d+)",re.IGNORECASE|re.DOTALL)

    m = mixednum.search(txt)
    n = fraction.search(txt)
    o = integer.search(txt)

    if m:
        return float(m.group(1))+(float(m.group(2))/float(m.group(3)))
    elif n:
        return float(n.group(1))/float(n.group(2))
    elif o:
        return float(o.group(1))
    else:
        return txt

Thanks!

+6  A: 

2.6 has the fractions module. Just split the string on whitespace, feed the chunks to fractions.Fraction(), call float() against the result, and add them all up.

Ignacio Vazquez-Abrams
Yup, should have spent more time on Google. Thanks for the tip!
Zach
Nah, just give the libref index a few reads so you can get used to what batteries are included.
Ignacio Vazquez-Abrams
+2  A: 

Ignacio's answer is probably the best way to handle it, but if you aren't using Python 2.6, you can build a function that does things a little more simply without having to rely on regular expressions. Here is a simple and not very robust version I threw together:

def parse_fraction(fraction):

    def parse_part(part):
        sections = part.split('/')
        if len(sections) == 1:
            return float(sections[0])
        return float(sections[0]) / float(sections[1])

    return sum( parse_part(part) for part in fraction.split() )

This obviously isn't perfect, because it will still accept input like '2 1/2 1/2', which it would evaluate as 3, since it essentially sums up a space delimited list of numbers while evaluating each as a fraction as necessary.

if you do stick with the regular expression based solution, you should know that you can use a raw string to avoid having to double backslash everything. Essentially, you can write:

mixednum = re.compile(r"(\d+) (\d+)/(\d+)")

The r in front of the string tells Python to not evaluate special characters within the string, so you can write literal backslashes and they will be treated as such. Also note that you do not need to escape the slash since it is not a special character in Python regular expressions (because it is not used to mark the borders of the literal regexp like in many languages). the re.IGNORECASE flag also doesn't make a lot of sense here, since it only includes numeric entities in the regexp, and re.DOTALL is also meaningless, since you have no dots for it to apply to.

Kent