次のようなテキストを階層ごとに取り出したいです。
aa(bb(dd)cc(ee))ff
これを
dict ={
aa:({bb:dd,cc:ee})
ff:None}
のように取り出したいです。
考えたこととしては
文字列を(で分割し、listにした上で(と)の間をnewlist = [n:m]で抽出してゆく
という方法を試しましたが、親と子の対応がうま作れません。
なんと検索すれば良いかもよくわからず、丸投げのような形になってしまい申し訳ありませんが、ご教授いただけませんでしょうか?
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答3件
0
複雑な構文なら、パーサーライブラリーを利用したほうがよいです。
ライブラリーを使わずに、単語分割部分から書いてみました。
a.py
python3'
1import re 2 3def break_token(line): 4 ans = [] 5 token = "" 6 for c in re.sub(" +", " ", line): 7 if c == "(" or c == ")": 8 if token != "": 9 ans.append(token) 10 token = "" 11 ans.append(c) 12 elif c == " " and token != "": 13 ans.append(token) 14 token = "" 15 else: 16 if c != " ": 17 token = token + c 18 tokem = "" 19 20 if token != "": 21 ans.append(token) 22 return ans 23 24def parse(tokens): 25 if len(tokens) == 0: 26 return None, [] 27 28 ans= {} 29 while len(tokens) > 0: 30 # print("---", tokens) 31 32 k = tokens[0] 33 if k == ")": 34 break 35 36 tokens = tokens[1::] 37 if len(tokens) < 1: 38 ans[k] = None 39 break 40 41 t = tokens[0] 42 if t == "(": 43 v, tokens = parse_val(tokens[1::]) 44 ans[k] = v 45 tokens = tokens[1::] 46 elif t == ")": 47 ans[k] = None 48 else: 49 ans[k] = "**ERR**" 50 51 return [ans, tokens] 52 53def parse_val(tokens): 54 if len(tokens) < 1: 55 return [None, []] 56 57 val = tokens[0] 58 if len(tokens) < 2: 59 return [val, []] 60 61 if tokens[1] != "(": 62 return [val, tokens[1::]] 63 64 val, tokens = parse(tokens) 65 return [val, tokens] 66 67 68tests =[ 69 "aa(bb(dd)cc(ee))ff(gg)", 70 "aa(bb(dd)cc(ee))ff", 71 "aa(bb(dd)cc(ee))", 72 "aa(bb(dd)cc)", 73 "aa(bb(dd))", 74 "aa(bb)cc(dd)ff", 75 "aa(bb)cc(dd)", 76 "aa(bb)xx", 77 "aa(bb)", 78 "aa", 79 "" 80] 81for line in tests: 82 print(line) 83 data, tokens = parse(break_token(line)) 84 print(" ->", data)
投稿2020/01/12 08:45
総合スコア22328
0
ベストアンサー
あまりスマートでは無いですけど、書いてみました。
Python
1import collections 2import re 3 4 5Brace = collections.namedtuple('Brace', ['start', 'end', 'level']) 6 7def find_braces(src): 8 stack = collections.deque() 9 braces = [] 10 11 level = 0 12 for i, ch in enumerate(src): 13 if ch == '(': 14 level += 1 15 stack.append(i) 16 elif ch == ')': 17 level -= 1 18 braces.append( 19 Brace(stack.pop(), i+1, level) 20 ) 21 22 return braces 23 24 25def parse(src): 26 if src == 'None': 27 return None 28 if re.fullmatch(r'[^()]+', src): 29 return src 30 31 # 32 if not src.endswith(')'): 33 src += '(None)' 34 35 outer_braces = [ 36 brace for brace in find_braces(src) 37 if brace.level == 0 38 ] 39 40 dst = {} 41 offset = 0 42 for start, end, _ in outer_braces: 43 key = src[offset:start] 44 val = parse( 45 src[start:end][1:-1] 46 ) 47 dst[key] = val 48 49 offset = end 50 51 return dst 52 53 54src = 'aa(bb(dd)cc(ee))ff' 55dst = parse(src) 56 57print(dst)
実行結果 Wandbox
{'aa': {'bb': 'dd', 'cc': 'ee'}, 'ff': None}
投稿2020/01/09 14:59
総合スコア35676
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

0
こんにちは
aa(bb(dd)cc(ee))ff
という文字列から、
json
1{ "aa":{ "bb":"dd", "cc":"ee" }, "ff":null }
というJSON形式の文字列を作り、これをパースするという方法はいかがでしょう?
以下は、その一例です。
python3
1import re 2import json 3 4 5def to_json_str(text): 6 text = re.compile(r'([a-z]+)$').sub('\1()', text) 7 text = text.replace('(', ':{') 8 text = text.replace(')', '},') 9 text = re.compile(r'{\s*([a-z]+)\s*}').sub('"\1"', text) 10 text = text.strip(',') 11 text = re.sub(r',\s*}', '}', text) 12 text = re.sub(r'{\s*}', 'null', text) 13 text = re.compile(r'([a-z]+):').sub('"\1":', text) 14 15 return '{%s}' % text 16 17 18json_str = to_json_str('aa(bb(dd)cc(ee))ff') # => {"aa":{"bb":"dd","cc":"ee"},"ff":null} 19 20dic = json.loads(json_str) 21 22print(dic) # => {'bb': 'dd', 'cc': 'ee'}, 'ff': None} 23
- 動作確認用Repl.it: https://repl.it/@jun68ykt/Q234191
関数 to_json_str
の本体は修正あるいはリファクタの余地があるかもしれませんが、JSONを経由するという考え方を提示することがこの回答の主旨になります。
参考になれば幸いです。
補足
以下、参考までに補足します。
なんと検索すれば良いかもよくわからず、
とのことですが、 aa(bb(dd)cc(ee))ff
のような、開くカッコと閉じるカッコがきちんと対応して出現するという規則に沿った文字列のパーサーを(私の回答コードのような既存モジュールの力を借りずに)自作するということになると、理屈としては
- 文脈自由文法
- プッシュダウンオートマトン
といった概念が(根本的なところでは)関わってくるものと思います。従って、何らかの文脈自由文法によって生成される文字列をパースするというテーマで検索すれば、参考になるコードがみつかるかもしれません。
投稿2020/01/09 13:56
編集2020/01/09 15:04総合スコア9058
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/01/14 04:00