34

一致する角かっこに含まれるテキストのチャンクを解析するためのPythonの最良の方法は何でしょうか?

"{ { a } { b } { { { c } } } }"

最初に戻る必要があります:

[ "{ a } { b } { { { c } } }" ]

それを入力として入れると、次のようになります。

[ "a", "b", "{ { c } }" ]

返されるはずです:

[ "{ c }" ]

[ "c" ]

[]
4

9 に答える 9

51

またはこのpyparsingバージョン:

>>> from pyparsing import nestedExpr
>>> txt = "{ { a } { b } { { { c } } } }"
>>>
>>> nestedExpr('{','}').parseString(txt).asList()
[[['a'], ['b'], [[['c']]]]]
>>>
于 2009-10-31T01:50:04.790 に答える
30

擬似コード:

For each string in the array:
    Find the first '{'. If there is none, leave that string alone.
    Init a counter to 0. 
    For each character in the string:  
        If you see a '{', increment the counter.
        If you see a '}', decrement the counter.
        If the counter reaches 0, break.
    Here, if your counter is not 0, you have invalid input (unbalanced brackets)
    If it is, then take the string from the first '{' up to the '}' that put the
     counter at 0, and that is a new element in your array.
于 2009-10-30T18:35:28.690 に答える
6

私はPythonに少し慣れていないので、気楽にやってください。ただし、これが機能する実装です。

def balanced_braces(args):
    parts = []
    for arg in args:
        if '{' not in arg:
            continue
        chars = []
        n = 0
        for c in arg:
            if c == '{':
                if n > 0:
                    chars.append(c)
                n += 1
            elif c == '}':
                n -= 1
                if n > 0:
                    chars.append(c)
                elif n == 0:
                    parts.append(''.join(chars).lstrip().rstrip())
                    chars = []
            elif n > 0:
                chars.append(c)
    return parts

t1 = balanced_braces(["{{ a } { b } { { { c } } } }"]);
print t1
t2 = balanced_braces(t1)
print t2
t3 = balanced_braces(t2)
print t3
t4 = balanced_braces(t3)
print t4

出力:

['{ a } { b } { { { c } } }']
['a', 'b', '{ { c } }']
['{ c }']
['c']
于 2009-10-30T19:26:54.450 に答える
5

使用して解析するlepl(経由でインストール可能$ easy_install lepl):

from lepl import Any, Delayed, Node, Space

expr = Delayed()
expr += '{' / (Any() | expr[1:,Space()[:]]) / '}' > Node

print expr.parse("{{a}{b}{{{c}}}}")[0]

出力:

ノード
 +-'{'
 +-ノード
 | +-'{'
 | +-'a'
 | `-'}'
 +-ノード
 | +-'{'
 | +-'b'
 | `-'}'
 +-ノード
 | +-'{'
 | +-ノード
 | | +-'{'
 | | +-ノード
 | | | +-'{'
 | | | +-'c'
 | | | `-'}'
 | | `-'}'
 | `-'}'
 `-'}'
于 2009-10-30T23:54:44.347 に答える
3

よりクリーンなソリューション。これにより、最も外側の角かっこで囲まれた文字列が返されます。Noneが返された場合、一致するものはありませんでした。

def findBrackets( aString ):
   if '{' in aString:
      match = aString.split('{',1)[1]
      open = 1
      for index in xrange(len(match)):
         if match[index] in '{}':
            open = (open + 1) if match[index] == '{' else (open - 1)
         if not open:
            return match[:index]
于 2010-05-06T10:51:05.403 に答える
2

少し奇妙ではなく{a}意味があると思いますが、一度にすべてを解析することもできます。フォーマットを正しく理解した場合:"a"["a"]

import re
import sys


_mbrack_rb = re.compile("([^{}]*)}") # re.match doesn't have a pos parameter
def mbrack(s):
  """Parse matching brackets.

  >>> mbrack("{a}")
  'a'
  >>> mbrack("{{a}{b}}")
  ['a', 'b']
  >>> mbrack("{{a}{b}{{{c}}}}")
  ['a', 'b', [['c']]]

  >>> mbrack("a")
  Traceback (most recent call last):
  ValueError: expected left bracket
  >>> mbrack("{a}{b}")
  Traceback (most recent call last):
  ValueError: more than one root
  >>> mbrack("{a")
  Traceback (most recent call last):
  ValueError: expected value then right bracket
  >>> mbrack("{a{}}")
  Traceback (most recent call last):
  ValueError: expected value then right bracket
  >>> mbrack("{a}}")
  Traceback (most recent call last):
  ValueError: unbalanced brackets (found right bracket)
  >>> mbrack("{{a}")
  Traceback (most recent call last):
  ValueError: unbalanced brackets (not enough right brackets)
  """
  stack = [[]]
  i, end = 0, len(s)
  while i < end:
    if s[i] != "{":
      raise ValueError("expected left bracket")
    elif i != 0 and len(stack) == 1:
      raise ValueError("more than one root")
    while i < end and s[i] == "{":
      L = []
      stack[-1].append(L)
      stack.append(L)
      i += 1
    stack.pop()
    stack[-1].pop()
    m = _mbrack_rb.match(s, i)
    if m is None:
      raise ValueError("expected value then right bracket")
    stack[-1].append(m.group(1))
    i = m.end(0)
    while i < end and s[i] == "}":
      if len(stack) == 1:
        raise ValueError("unbalanced brackets (found right bracket)")
      stack.pop()
      i += 1
  if len(stack) != 1:
    raise ValueError("unbalanced brackets (not enough right brackets)")
  return stack[0][0]


def main(args):
  if args:
    print >>sys.stderr, "unexpected arguments: %r" % args
  import doctest
  r = doctest.testmod()
  print r
  return r[0]

if __name__ == "__main__":
  sys.exit(main(sys.argv[1:]))
于 2009-10-30T19:42:02.487 に答える
2

パーサー(この場合はlepl)を使用したいが、最終的な解析リストではなく中間結果が必要な場合は、これがあなたが探していた種類のものだと思います。

>>> nested = Delayed()
>>> nested += "{" + (nested[1:,...]|Any()) + "}"
>>> split = (Drop("{") & (nested[:,...]|Any()) & Drop("}"))[:].parse
>>> split("{{a}{b}{{{c}}}}")
['{a}{b}{{{c}}}']
>>> split("{a}{b}{{{c}}}")
['a', 'b', '{{c}}']
>>> split("{{c}}")
['{c}']
>>> split("{c}")
['c']

最初は不透明に見えるかもしれませんが、実際にはかなり単純です:o)

ネストされたものは、ネストされたブラケットのマッチャーの再帰的定義です(定義内の「+」と[...]は、一致した後、すべてを単一の文字列として保持します)。次に、splitは、 "{" ... "}"( "Drop"で破棄)で囲まれ、ネストされた式または任意の文字を含むものを可能な限り一致させる( "[:]")と言います。

最後に、これが「オールインワン」パーサーのleplバージョンで、上記の構文解析の例と同じ形式で結果が得られますが、入力でのスペースの表示方法についてはより柔軟です。

>>> with Separator(~Space()[:]):
...     nested = Delayed()
...     nested += Drop("{") & (nested[1:] | Any()) & Drop("}") > list
...
>>> nested.parse("{{ a }{ b}{{{c}}}}")
[[['a'], ['b'], [[['c']]]]]
于 2009-10-31T12:38:37.067 に答える
1

Grako(文法コンパイラ)の使用:

#!/usr/bin/env python
import json
import grako # $ pip install grako

grammar_ebnf = """
    bracketed = '{' @:( { bracketed }+ | any ) '}' ;
    any = /[^{}]+?/ ;
"""
model = grako.genmodel("Bracketed", grammar_ebnf)
ast = model.parse("{ { a } { b } { { { c } } } }", "bracketed")
print(json.dumps(ast, indent=4))

出力

[
    "a", 
    "b", 
    [
        [
            "c"
        ]
    ]
]
于 2014-10-05T18:17:45.743 に答える
1

これは、同様のユースケースのために私が思いついた解決策です。これは、受け入れられた疑似コードの回答に大まかに基づいていました。外部ライブラリの依存関係を追加したくありませんでした。

def parse_segments(source, recurse=False):
    """
    extract any substring enclosed in parenthesis
    source should be a string
    """
    unmatched_count = 0
    start_pos = 0
    opened = False
    open_pos = 0
    cur_pos = 0

    finished = []
    segments = []

    for character in source:
        #scan for mismatched parenthesis:
        if character == '(':
            unmatched_count += 1
            if not opened:
                open_pos = cur_pos
            opened = True

        if character == ')':
            unmatched_count -= 1

        if opened and unmatched_count == 0:
            segment = source[open_pos:cur_pos+1]
            segments.append(segment)
            clean = source[start_pos:open_pos]
            if clean:
                finished.append(clean)
            opened = False
            start_pos = cur_pos+1

        cur_pos += 1

    assert unmatched_count == 0

    if start_pos != cur_pos:
        #get anything that was left over here
        finished.append(source[start_pos:cur_pos])

    #now check on recursion:
    for item in segments:
        #get rid of bounding parentheses:
        pruned = item[1:-1]
        if recurse:
            results = parse_tags(pruned, recurse)
            finished.expand(results)
        else:
            finished.append(pruned)

    return finished
于 2014-12-08T15:22:17.517 に答える