如何使用pyparsing解析分数表达式?

5 投票
4 回答
2780 浏览
提问于 2025-04-16 05:21

我们刚开始尝试使用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 个回答

2

这个方法可能会对你有帮助:

可以看看大约在第39行的内容:

mixed = Combine(numeral + fraction, adjacent=False, joinString=' ')
3

这可能不是你想要的答案,但...

>>> import fractions
>>> txt= "1 1/2"
>>> sum( map( fractions.Fraction, txt.split() ) )
Fraction(3, 2)
>>> float(_)
1.5
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是从左到右解析的,它不会像正则表达式那样进行回溯。所以这个表达式可能效果不好:

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只会执行你告诉它的前瞻,否则它就是简单的从左到右解析。

撰写回答