2012-02-14 1 views
3

が、私はこのような文字列があるとします。Python、かっこ内にあるものを無視して、key = valueリストを解析するにはどうすればよいですか?

"key1=value1;key2=value2;key3=(key3.1=value3.1;key3.2=value3.2)" 

私はkey3の値は、文字列

"(key3.1=value3.1;key3.2=value3.2)" 

、最終的には、対応するサブある上記に対応する辞書を取得したいと思います辞書。

私はセミコロンで文字列を分割する方法を知っていますが、括弧の間にセミコロンを無視するようにパーサーに指示する方法はありますか?
これには、潜在的にネストされたカッコが含まれます。

現在、私はマッチング括弧のペアを探しアドホックルーチンを使用しています、「クリア」、その内容、位置を分割し、元の文字列に適用しますが、これは非常にエレガント表示されない、いくつか存在しなければなりませんこれを行うためにあらかじめパッケージ化されたpythonic方法。

誰もが興味を持っている場合は、ここで私は現在、使用していますコードです:

def pparams(parameters, sep=';', defs='=', brc='()'): 
    ''' 
    unpackages parameter string to struct 
    for example, pippo(a=21;b=35;c=pluto(h=zzz;y=mmm);d=2d3f) becomes: 
    a: '21' 
    b: '35' 
    c.fn: 'pluto' 
    c.h='zzz' 
    d: '2d3f' 
    fn_: 'pippo' 
    ''' 

    ob=strfind(parameters,brc[0]) 
    dp=strfind(parameters,defs) 
    out={} 

    if len(ob)>0: 
     if ob[0]<dp[0]: 
      #opening function 
      out['fn_']=parameters[:ob[0]] 
      parameters=parameters[(ob[0]+1):-1] 
    if len(dp)>0: 
     temp=smart_tokenize(parameters,sep,brc); 
     for v in temp: 
      defp=strfind(v,defs) 
      pname=v[:defp[0]] 
      pval=v[1+defp[0]:] 
      if len(strfind(pval,brc[0]))>0: 
       out[pname]=pparams(pval,sep,defs,brc); 
      else: 
       out[pname]=pval 
    else: 
     out['fn_']=parameters 
    return out 

def smart_tokenize(instr, sep=';', brc='()'): 
    ''' 
    tokenize string ignoring separators contained within brc 
    ''' 
    tstr=instr; 
    ob=strfind(instr,brc[0]) 
    while len(ob)>0: 
     cb=findclsbrc(tstr,ob[0]) 
     tstr=tstr[:ob[0]]+'?'*(cb-ob[0]+1)+tstr[cb+1:] 
     ob=strfind(tstr,brc[1]) 
    sepp=[-1]+strfind(tstr,sep)+[len(instr)+1] 
    out=[] 
    for i in range(1,len(sepp)): 
     out.append(instr[(sepp[i-1]+1):(sepp[i])]) 
    return out 

def findclsbrc(instr, brc_pos, brc='()'): 
    ''' 
    given a string containing an opening bracket, finds the 
    corresponding closing bracket 
    ''' 
    tstr=instr[brc_pos:] 
    o=strfind(tstr,brc[0]) 
    c=strfind(tstr,brc[1]) 
    p=o+c 
    p.sort() 
    s1=[1 if v in o else 0 for v in p] 
    s2=[-1 if v in c else 0 for v in p] 
    s=[s1v+s2v for s1v,s2v in zip(s1,s2)] 
    s=[sum(s[:i+1]) for i in range(len(s))] #cumsum 
    return p[s.index(0)]+brc_pos 

def strfind(instr, substr): 
    ''' 
    returns starting position of each occurrence of substr within instr 
    ''' 
    i=0 
    out=[] 
    while i<=len(instr): 
     try: 
      p=instr[i:].index(substr) 
      out.append(i+p) 
      i+=p+1 
     except: 
      i=len(instr)+1 
    return out 
+6

あなたはJSONを使用することができます標準の永続化形式 –

+0

を使用する方がはるかに簡単だろうか? – jterrace

答えて

6

をあなたは本当のパーサを構築したい場合は、PLYまたはPyParsingのように、Python parsing librariesのいずれかを使用しますが。このような本格的な図書館が手近な作業のために過度のものであると分かった場合は、既に持っているもののようなハックをしてください。私は確かに、外部ライブラリなしできれいな数行のソリューションがないと確信しています。

+0

私はそれがあまりにも複雑ではない正規表現でやっていると確信しています。編集:Kasey Kirkhamそれに私を打つ! – Voo

+0

@Voo:それは不可能だとは決して言わなかった。ただし、ネストされたカッコを正規表現で解析することは常にハックです。キーや値の引用符を追加しようとすると、状況がかなり悪くなります。 –

+0

あなたはそれを行うことができますが、いくつかのトリック(元の投稿の中に入れ子についての部分を見逃しました!) - 「あまりにも多くの正規の正規表現」のおかげで。個人的には、数行のコードに対して完全な解析ライブラリを使用するのはちょっと残念だと思われるため、混合正規表現と反復処理のソリューションを使用します。 – Voo

0

あなたはグループをキャプチャするために正規表現を使用することができます。

>>> import re 
>>> s = "key1=value1;key2=value2;key3=(key3.1=value3.1;key3.2=value3.2)" 
>>> r = re.compile('(\w+)=(\w+|\([^)]+\));?') 
>>> dict(r.findall(s)) 

をこの正規表現は言う:

(\w)+ # Find and capture a group with 1 or more word characters (letters, digits, underscores) 
= # Followed by the literal character '=' 
(\w+ # Followed by a group with 1 or more word characters 
|\([^)]+\) # or a group that starts with an open paren (parens escaped with '\(' or \')'), followed by anything up until a closed paren, which terminates the alternate grouping 
);? # optionally this grouping might be followed by a semicolon. 

お奨めは、奇妙な文法のようなものを言います。より標準的な形式を使用することを検討する必要があります。ガイダンスが必要な場合は、別の質問をするかもしれません。がんばろう!スヴェンMarnachの答えに拡大

+0

正規表現に 're.VERBOSE'を渡して、実際の正規表現にすべてのコメントを含めるべきだと思います。それらをはるかに読みやすくし、実際には使用されていない機能ですが、使用時には必須です。それ以外の場合は非常にいい! – Voo

+2

これは、別のレベルの入れ子を追加するとすぐに機能しなくなります。ネストされたカッコを正規表現で解析することは、*最も内側の*式から始まり、パーズツリーを操作することによってのみ可能です。それはいつもハッキーだろうから、私はこれがOPが既に持っているものよりも良くなければならない方法を見ない。 –

1

は、ここであなたのために働く必要がありpyparsing文法の例です:

from pyparsing import (ZeroOrMore, Word, printables, Forward, 
         Group, Suppress, Dict) 


collection = Forward() 
simple_value = Word(printables, excludeChars='()=;') 
key = simple_value 
inner_collection = Suppress('(') + collection + Suppress(')') 
value = simple_value^inner_collection 
key_and_value = Group(key + Suppress('=') + value) 
collection << Dict(key_and_value + ZeroOrMore(Suppress(';') + key_and_value)) 


coll = collection.parseString(
    "key1=value1;key2=value2;key3=(key3.1=value3.1;key3.2=value3.2)") 

print coll['key1']    # value1 
print coll['key2']    # value2 
print coll['key3']['key3.1'] # value3.1 
関連する問題