2010-10-12 31 views
4

我們剛剛開始踢輪胎pyparsing,並喜歡它,但我們一直無法得到它來幫助我們解析分數字符串以將它們轉換爲數字數據類型。如何使用pyparsing解析小數表達式?

例如,如果在數據庫表中的列值包含字符串:

1 1/2

我們想一些方法將其轉換成數字相當於蟒:

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)。提示?提示?及時的智慧? :)

回答

6

由於您引用了一些測試,這聽起來像您至少已經採取了刺探問題。我假設你已經定義的單號,可以是整數或實 - 不要緊,要轉換的一切反正浮動 - 和兩個數字的一​​小部分,大概是這樣的:

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) 

(注意使用解析動作,也做浮點轉換和分數劃分權在分析時的,我更喜歡這樣做,而解析,當我知道東西是不是數字或分數或什麼的,而不是稍後再回來並通過了一堆零散的字符串篩選,試圖重新解析器已經做好了識別邏輯。)

下面是測試情況下,我的COM提出您的問題,由一個整數,分數和整數和分數,同時使用整數和實數:

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」,W HICH 匹配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只會做你告訴它的前瞻,否則它只是直接從左到右的解析。

+0

真棒,很好的答案,謝謝你花時間拼出來!我們得到了測試用例:1/2 [1.0],並且爲什麼我們一直得到1而不是0.5。它看起來像你勾畫了我們的絆腳石。數據本身非常混亂,但看起來這至少可以爲我們打下堅實​​的基礎,並列舉其他最常見的客戶產品屬性值表達式。 :) – Xavian 2010-10-12 19:28:29

2

這個配方可能會有所幫助:

看看周圍39行:

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

感謝這個,這是一個有趣的食譜,可以幫助我們進行類似的分析問題。 (產品屬性的實體檢測「12伏直流電機」)不幸的是,當我們嘗試使用這個代碼時,它會拋出一個錯誤。在我們修正了錯誤之後,它似乎並沒有像預期的那樣工作,但我們將繼續關注它,因爲它是我們正在研究的類似問題如何使用pyparsing的一個例子。 :) 謝謝! – Xavian 2010-10-12 19:07:06

3

不正是你要找的內容,但是...

>>> import fractions 
>>> txt= "1 1/2" 
>>> sum(map(fractions.Fraction, txt.split())) 
Fraction(3, 2) 
>>> float(_) 
1.5 
+0

哇,這真的很好,很優雅,我不敢相信我們忽略了這一點。 :)不幸的是,我們正在處理的源數據非常混亂並且很難處理,所以如此緊張的事情不太可能奏效。有時我們會看到諸如「1 1/2」或「〜1 1/2」之類的東西,或者瘋狂地稱之爲「1 1/8〜2 7/8」。我們需要一個基本的分析器來開始,然後重構覆蓋我們應對的遺留數據中最常見的情況。 – Xavian 2010-10-12 19:13:41

1

這是一種與S.洛特一倍,但這裏是它無論如何:

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))) 
+0

超級簡潔。:)如果只有我們的數據更清潔,我們才能夠使用這種方法。 :) – Xavian 2010-10-12 19:25:21

+0

+1爲您考慮意想不到的地方的空白。 Pyparsing確實也隱含了空白跳過,因此您添加的測試可以與上面的pyparsing解析器一起工作,而無需其他更改。 – PaulMcG 2010-11-21 15:50:15