2013-10-11 133 views
3

我想解析生物序列中位置的特定語法。該位置可以有這樣的形式:用pyparsing解析嵌套結構

12   -- a simple position in the sequence 
12+34  -- a complex position as a base (12) and offset(+34) 
12_56  -- a range, from 12 to 56 
12+34_56-78 -- a range as a start to end, where either or both may be simple or complex 

我想有這些解析爲類型的字典,大致是這樣的:

12   -> { 'start': { 'base': 12, 'offset': 0 }, 'end': None } 
12+34  -> { 'start': { 'base': 12, 'offset': 34 }, 'end': None } 
12_56  -> { 'start': { 'base': 12, 'offset': 0 }, 
        'end': { 'base': 56, 'offset': 0 } } 
12+34_56-78 -> { 'start': { 'base': 12, 'offset': 0 }, 
        'end': { 'base': 56, 'offset': -78 } } 

我做了使用pyparsing幾個刺。這裏有一個:

from pyparsing import * 
integer = Word(nums) 
signed_integer = Word('+-', nums) 
underscore = Suppress('_') 
position = integer.setResultsName('base') + Or(signed_integer,Empty).setResultsName('offset') 
interval = position.setResultsName('start') + Or(underscore + position,Empty).setResultsName('end') 

的結果是接近我想要的東西:

In [20]: hgvspyparsing.interval.parseString('12-34_56+78').asDict() 
Out[20]: 
{'base': '56', 
'end': (['56', '+78'], {'base': [('56', 0)], 'offset': [((['+78'], {}), 1)]}), 
'offset': (['+78'], {}), 
'start': (['12', '-34'], {'base': [('12', 0)], 'offset': [((['-34'], {}), 1)]})} 

兩個問題:

  1. asDict()只能在根parseResult工作。有沒有辦法來pyparsing pyparsing返回一個嵌套的字典(和唯一的)?

  2. 如何獲得範圍末端和位置偏移的可選性?位置規則中的Or()不會削減它。 (我在範圍的末尾嘗試類似)。理想情況下,我將所有職位視爲最複雜形式(即{start:{base,end},end:{base,end}})的特殊情況,其中較簡單的情況使用0或無)。

謝謝!

+1

而且'或''和''MatchFirst'和'Each'都以列表表達式作爲它們的參數,而不僅僅是表達式。這就是爲什麼我更喜歡操作符覆蓋:'expr1 + expr2 | expr3'比MatchFirst([And([expr1,expr2]),expr3])'更容易遵循。 – PaulMcG

回答

3

一些一般pyparsing提示:

Or(expr, empty)更好寫成Optional(expr)。另外,你的Or表達式試圖創建一個Or類,其類型爲Empty,你可能想寫第Empty()empty作爲第二個參數。

expr.setResultsName("name")如果你想申請結構的結果,使用Group現在可以寫爲:expr("name")

使用dump()而不是asDict()可更好地查看分析結果的結構。

這是我怎麼會建立起自己的表達:

from pyparsing import Word, nums, oneOf, Combine, Group, Optional 

integer = Word(nums) 

sign = oneOf("+ -") 
signedInteger = Combine(sign + integer) 

integerExpr = Group(integer("base") + Optional(signedInteger, default="0")("offset")) 

integerRange = integerExpr("start") + Optional('_' + integerExpr("end")) 


tests = """\ 
12 
12+34 
12_56 
12+34_56-78""".splitlines() 

for t in tests: 
    result = integerRange.parseString(t) 
    print t 
    print result.dump() 
    print result.asDict() 
    print result.start.base, result.start.offset 
    if result.end: 
     print result.end.base, result.end.offset 
    print 

打印:

12 
[['12', '0']] 
- start: ['12', '0'] 
    - base: 12 
    - offset: 0 
{'start': (['12', '0'], {'base': [('12', 0)], 'offset': [('0', 1)]})} 
12 0 

12+34 
[['12', '+34']] 
- start: ['12', '+34'] 
    - base: 12 
    - offset: +34 
{'start': (['12', '+34'], {'base': [('12', 0)], 'offset': [('+34', 1)]})} 
12 +34 

12_56 
[['12', '0'], '_', ['56', '0']] 
- end: ['56', '0'] 
    - base: 56 
    - offset: 0 
- start: ['12', '0'] 
    - base: 12 
    - offset: 0 
{'start': (['12', '0'], {'base': [('12', 0)], 'offset': [('0', 1)]}), 'end': (['56', '0'], {'base': [('56', 0)], 'offset': [('0', 1)]})} 
12 0 
56 0 

12+34_56-78 
[['12', '+34'], '_', ['56', '-78']] 
- end: ['56', '-78'] 
    - base: 56 
    - offset: -78 
- start: ['12', '+34'] 
    - base: 12 
    - offset: +34 
{'start': (['12', '+34'], {'base': [('12', 0)], 'offset': [('+34', 1)]}), 'end': (['56', '-78'], {'base': [('56', 0)], 'offset': [('-78', 1)]})} 
12 +34 
56 -78 
1

實際的語法比你的例子更復雜嗎?由於解析可以相當容易地在純Python來完成:

bases = ["12", "12+34", "12_56", "12+34", "12+34_56-78"] 

def parse_base(base_string): 

    def parse_single(s): 
     if '-' in s: 
      offset_start = s.find("-") 
      base, offset = int(s[:offset_start]), int(s[offset_start:]) 
     elif '+' in s: 
      offset_start = s.find("+") 
      base, offset = int(s[:offset_start]), int(s[offset_start:]) 
     else: 
      base = int(s) 
      offset = 0 
     return {'base': base, 'offset': offset} 

    range_split = base_string.split('_') 
    if len(range_split) == 1: 
     start = range_split[0] 
     return {'start': parse_single(start), 'end': None} 
    elif len(range_split) == 2: 
     start, end = range_split 
     return {'start': parse_single(start), 
       'end': parse_single(end)} 

輸出:

for b in bases: 
    print(parse_base(b)) 

{'start': {'base': 12, 'offset': 0}, 'end': None} 
{'start': {'base': 12, 'offset': 34}, 'end': None} 
{'start': {'base': 12, 'offset': 0}, 'end': {'base': 56, 'offset': 0}} 
{'start': {'base': 12, 'offset': 34}, 'end': None} 
{'start': {'base': 12, 'offset': 34}, 'end': {'base': 56, 'offset': -78}} 
+0

是的,有很多其他解析要做,地點問題阻止了我。Paul McGuire的解決方案更符合我的需求。 – Reece

+0

很好地完成! +1使用所有字符串函數,而不訴諸正則表達式。 – PaulMcG