Python解析括号中的块

Python解析括号中的块,python,parsing,text-parsing,brackets,Python,Parsing,Text Parsing,Brackets,Python中解析匹配括号中包含的文本块的最佳方法是什么 "{ { a } { b } { { { c } } } }" 应首先返回: [ "{ a } { b } { { { c } } }" ] 将其作为输入应返回: [ "a", "b", "{ { c } }" ] [ "{ c }" ] [ "c" ] [] 应返回: [ "a", "b", "{ { c } }" ] [ "{ c }" ] [ "c" ] [] 伪代码: For each string in th

Python中解析匹配括号中包含的文本块的最佳方法是什么

"{ { a } { b } { { { c } } } }"
应首先返回:

[ "{ a } { b } { { { c } } }" ]
将其作为输入应返回:

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

[ "c" ]

[]
应返回:

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

[ "c" ]

[]
伪代码:

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.

我对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']

您也可以同时解析它们,尽管我发现
{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:]))
重新导入
导入系统
_mbrack_rb=re.compile(([^{}]*)})#re.match没有pos参数
德夫姆布拉克(s):
“”“解析匹配的括号。
>>>姆布拉克(“{a}”)
“a”
>>>姆布拉克(“{a}{b}”)
['a','b']
>>>姆布拉克(“{a}{b}{{{{c}}}”)
[a'、[b'、[c']]
>>>姆布拉克(“a”)
回溯(最近一次呼叫最后一次):
ValueError:应为左括号
>>>姆布拉克(“{a}{b}”)
回溯(最近一次呼叫最后一次):
ValueError:多个根目录
>>>姆布拉克(“a”)
回溯(最近一次呼叫最后一次):
ValueError:预期值,然后右括号
>>>姆布拉克(“{a{}}”)
回溯(最近一次呼叫最后一次):
ValueError:预期值,然后右括号
>>>姆布拉克(“{a}}”)
回溯(最近一次呼叫最后一次):
ValueError:不平衡的括号(找到右括号)
>>>姆布拉克(“{a}”)
回溯(最近一次呼叫最后一次):
ValueError:括号不平衡(右括号不够)
"""
堆栈=[]]
i、 结束=0,长(s)
当我结束时:
如果s[i]!=“{”:
提升值错误(“预期左括号”)
如果i!=0且len(堆栈)==1:
提升值错误(“多个根”)
当i>sys.stderr,“意外参数:%r”%args
进口医生测试
r=doctest.testmod()
印刷机
返回r[0]
如果名称=“\uuuuu main\uuuuuuuu”:
系统出口(主(系统argv[1:]))
使用解析(可通过
$easy\u install lepl安装):

输出:

Node +- '{' +- Node | +- '{' | +- 'a' | `- '}' +- Node | +- '{' | +- 'b' | `- '}' +- Node | +- '{' | +- Node | | +- '{' | | +- Node | | | +- '{' | | | +- 'c' | | | `- '}' | | `- '}' | `- '}' `- '}' 节点 +- '{' +-节点 | +- '{' |+-“a” | `- '}' +-节点 | +- '{' |+-“b” | `- '}' +-节点 | +- '{' |+节点 | | +- '{' ||+-节点 | | | +- '{' || |+-“c” | | | `- '}' | | `- '}' | `- '}' `- '}'
或此版本:

>>> from pyparsing import nestedExpr
>>> txt = "{ { a } { b } { { { c } } } }"
>>>
>>> nestedExpr('{','}').parseString(txt).asList()
[[['a'], ['b'], [[['c']]]]]
>>>

如果您想使用解析器(本例中为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)

nested是嵌套方括号的匹配器的递归定义(定义中的“+”和[…]在匹配后将所有内容保持为单个字符串)。然后split表示尽可能多地匹配由“{”…}”包围的内容([:])(我们用“Drop”丢弃)并包含嵌套表达式或任何字母

最后,这里是一个lepl版本的“一体式”解析器,它以与上述pyparsing示例相同的格式给出结果,但(我认为)在输入中如何显示空格方面更灵活:

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

更干净的解决方案。这将找到包含在最外层括号中的返回字符串。如果未返回任何字符串,则不存在匹配项

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]
使用:

输出
以下是我为类似用例提出的解决方案。这是基于公认的psuedo代码答案的松散解决方案。我不想为外部库添加任何依赖项:

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

我很好奇。在你的上一个例子中,它会返回[{c}],然后它会返回[“c”],然后它会返回[]?第二个语句不是应该是[{a}{b}{{{{c}}}}]吗?第二个语句是[{a}{b}{{{c}}}],正是你写的…我将完成整个序列,让人们确切地知道我想你想要什么,[{a}{b}{{{c}}}{{c}}}{c}}}{c}}},是吗?太好了!我脑子里有这样的设计,但不是很好地表达出来。出于兴趣,这种解析器有正式名称吗?我熟悉我们在uni做的一个模块中的递归下降解析器,但不是这种。我不确定它是否有名称。这可能只是做p的一种短路方式arens匹配,而不进行全面的递归下降解析器。也许其他人知道得更好。添加:计数器表示“递归”的深度,而不必进行函数调用。缩进已关闭。如前所述,输入“{a}{b}”时,您将返回“a{b}”,而不是“ab”。“Here”部分需要缩进。这是Python和缩进计数。不,我认为他的缩进是正确的。如果循环终止时计数器等于零,则一切正常,并且找到了匹配的括号对。但是,如果它终止时计数器!=零,则循环只是终止,因为没有更多字符,因此括号是不匹配的。这比公认答案中的算法可读性强得多,而且我还相信一个广泛使用的(因此经过测试)算法我自己开发的解决方案上的库。需要注意的一件非常重要的事情是:如果整个表达式没有包含在一对分组符号中,那么这将只处理第一个分组的表达式。如果总是要处理整个表达式,则可以通过在不包含分组符号时添加一对外部分组符号来强制执行此操作塔尔
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