5

到目前为止,我们刚刚开始尝试 pyparsing 并喜欢它,但我们无法让它帮助我们解析小数字符串以将它们转换为数字数据类型。

例如,如果数据库表中的列值包含字符串:

1 1/2

我们想要某种方法将其转换为数字 python 等价物:

1.5

我们想做一个不关心分数中的数字是整数还是实数的解析器。例如,我们想要:

1.0 1.0/2.0

...仍然翻译为:

1.5

本质上,我们希望解析器在概念上执行以下操作:

“1 1/2” = 1 + 0.5 = 1.5

下面的示例代码似乎让我们接近......

http://pyparsing.wikispaces.com/file/view/parsePythonValue.py

...但距离不够近,无法取得进展。我们制作小数处理程序的所有测试仅返回表达式 (1) 的第一部分。提示?提示?及时的智慧?:)

4

4 回答 4

8

既然你引用了一些测试,听起来你至少已经解决了这个问题。我假设您已经定义了一个数字,它可以是整数或实数 - 没关系,无论如何您都将所有内容转换为浮点数 - 以及两个数字的一​​小部分,可能是这样的:

from pyparsing import Regex, Optional

number = Regex(r"\d+(\.\d*)?").setParseAction(lambda t: float(t[0]))

fraction = number("numerator") + "/" + number("denominator")
fraction.setParseAction(lambda t: t.numerator / t.denominator)

(注意解析操作的使用,它在解析时进行浮点转换和小数除法。我更喜欢在解析时这样做,当我知道某事是数字或分数或其他东西时,而不是稍后回来并筛选通过一堆零散的字符串,试图重新创建解析器已经完成的识别逻辑。)

这是我为您的问题编写的测试用例,由整数、分数、整数和分数组成,使用整数和实数:

tests = """\
1
1.0
1/2
1.0/2.0
1 1/2
1.0 1/2
1.0 1.0/2.0""".splitlines()

for t in tests:
    print t, fractExpr.parseString(t)

最后一步是如何定义一个分数表达式,它可以是一个数字、一个分数,或者一个数字和一个分数。

由于 pyparsing 是从左到右的,它不会像 regexen 那样进行回溯。所以这个表达式不会很好地工作:

fractExpr = Optional(number) + Optional(fraction)

要将可能来自数字和小数部分的数值相加,请添加以下解析操作:

fractExpr.setParseAction(lambda t: sum(t))

我们的测试打印出来:

1 [1.0]
1.0 [1.0]
1/2 [1.0]
1.0/2.0 [1.0]
1 1/2 [1.5]
1.0 1/2 [1.5]
1.0 1.0/2.0 [1.5]

对于仅包含一个分数的测试用例1/2,前导分子与该Optional(number)术语匹配,但我们只剩下“/2”,它与 -匹配Optional(fraction)- 幸运的是,由于第二个术语是可选的,因此“通过” ,但它并没有真正做到我们想要的。

我们需要让 fractExpr 更智能一点,并让它首先寻找一个孤立的分数,因为在孤立的数字和分数的前导分子之间存在这种潜在的混淆。最简单的方法是让 fractExpr 读取:

fractExpr = fraction | number + Optional(fraction)

现在有了这个改变,我们的测试结果更好了:

1 [1.0]
1.0 [1.0]
1/2 [0.5]
1.0/2.0 [0.5]
1 1/2 [1.5]
1.0 1/2 [1.5]
1.0 1.0/2.0 [1.5]

pyparsing 有几个经典的陷阱,这就是其中之一。请记住,pyparsing 仅执行您告诉它的前瞻,否则它只是从左到右的直接解析。

于 2010-10-12T12:35:59.450 回答
3

不正是您要找的东西,但是...

>>> import fractions
>>> txt= "1 1/2"
>>> sum( map( fractions.Fraction, txt.split() ) )
Fraction(3, 2)
>>> float(_)
1.5
于 2010-10-12T10:25:31.183 回答
2

这个食谱可能会有所帮助:

环顾第 39 行:

mixed = Combine(numeral + fraction, adjacent=False, joinString=' ')
于 2010-10-12T05:32:22.477 回答
1

这是 S. Lott 的双倍,但无论如何都是这样:

from fractions import Fraction
print sum(Fraction(part) for part in '1 1/2'.split())

但是,处理浮点“整数”会非常复杂:

from fractions import Fraction
clean = '1.0 1.0/2.0'.replace('.0 ',' ').replace('.0/', '/').rstrip('0.').split()
print(clean)
print(sum(Fraction(part) for part in clean))

和其他海报的例子,加上一个带有/的空格:

from fractions import Fraction

tests = """\
1
1.0
1/2
1.0/2.0
1 1/2
1.0 1/2
1.0 1.0/2.0
1.0 1.0 / 2.0
""".splitlines()

for t in tests:
    clean = t.replace('.0 ',' ').replace('.0/', '/').rstrip('0.').split()
    value = sum(Fraction(part) for part in clean)
    print('%s -> %s, %s = %f' % (t, clean, value, float(value)))
于 2010-10-12T10:46:04.377 回答