【问题标题】:Elegant parsing of this? "a,b,c",d,"e,f"优雅地解析这个? "a,b,c",d,"e,f"
【发布时间】:2010-07-09 15:45:18
【问题描述】:

我希望将这些类型的字符串解析为 Python 中的列表:

"a,b,c",d,"e,f"        =>  ['a','b','c'] , ['d'] , ['e','f']
"a,b,c",d,e            =>  ['a','b','c'] , ['d'] , ['e']
a,b,"c,d,e,f"          =>  ['a'],['b'],['c','d','e','f']
a,"b,c,d",{x(a,b,c-d)} =>  ['a'],['b','c','d'],[('x',['a'],['b'],['c-d'])]

它嵌套了,所以我怀疑正则表达式已经过时了。我能想到的就是开始计算引号和括号来解析它,但这似乎非常不雅。或者可能首先匹配引号并用 somechar 替换它们之间的逗号,然后用逗号分割,直到所有嵌套完成,最后在 somechar 上重新分割。

有什么想法吗?

【问题讨论】:

  • 看起来你需要一个解析器生成器。不过我不知道 Python 的。
  • 有一堆Python解析器生成器:nedbatchelder.com/text/python-parsers.html
  • 你到底怎么称呼'嵌套'。给定示例中是否已经存在嵌套?否则,引号必须以某种方式在嵌套结构中进行转义。
  • 你在那里输入了一个漂亮的、不合逻辑的数据输入——如果你的输出必须看起来相对丑陋(特别是最后一种情况)——我认为不可能有“优雅”的解决方案。弄脏你的手,然后自己解析。使用状态变量来知道你正在解析的字符是什么,等等......应该在 Python 中的 30-50 行解析器中运行(不使用任何解析器生成器或其他东西)
  • 为什么最后一个例子中的c-d变成['c-d']而不是['c','-','d']?如果它也是顶级的,会是这样吗?

标签: python string parsing


【解决方案1】:

所以,你来了,你的“诚实的 python 解析器”。为您编码而不是回答问题,但是如果您使用它我会很好:-)

QUOTE = '"'
SEP = ',(){}"'
S_BRACKET = '{'
E_BRACKET = '}'
S_PAREN = '('

def parse_plain(string):
    counter = 0
    token = ""
    while counter<len(string):
        if string[counter] in SEP:
            counter += 1
            break
        token += string[counter]
        counter += 1
    return counter, token

def parse_bracket(string):
    counter = 1
    fwd, token = parse_plain(string[counter:])
    output = [token]
    counter += fwd
    fwd, token = parse_(string[counter:])
    output += token
    counter += fwd
    output = [tuple(output)]
    return counter, output

def parse_quote(string):
    counter = 1
    output = []
    while counter<len(string):
        if counter > 1 and string[counter - 1] == QUOTE:
            counter += 1
            break
        fwd, token = parse_plain(string[counter:])
        output.append(token)
        counter += fwd
    return counter, output

def parse_(string):
    output = []
    counter = 0
    while counter < len(string):
        if string[counter].isalpha():
            fwd, token = parse_plain(string[counter:])
            token = [token]
        elif string[counter] == QUOTE:
            fwd, token = parse_quote(string[counter:])
        elif string[counter] == S_BRACKET:
            fwd, token = parse_bracket(string[counter:])
        elif string[counter] == E_BRACKET:
            counter += 1
            break
        else:
            counter += 1
            continue
        output.append(token)
        counter += fwd
    return counter, output

def parse(string):
    return parse_(string)[1]

并测试输出:

>>> print parse('''"a,b,c",d,"e,f"''')
[['a', 'b', 'c'], ['d'], ['e', 'f']]
>>> print parse('''"a,b,c",d,e ''')
[['a', 'b', 'c'], ['d'], ['e ']]
>>> print parse('''a,b,"c,d,e,f"''')
[['a'], ['b'], ['c', 'd', 'e', 'f']]
>>> print parse('''a,"b,c,d",{x(a,b,c-d)}''')
[['a'], ['b', 'c', 'd'], [('x', ['a'], ['b'], ['c-d'])]]
>>> print parse('''{x(a,{y("b,c,d",e)})},z''')
[[('x', ['a'], [('y', ['b', 'c', 'd'], ['e'], ['z'])])]]
>>>

【讨论】:

  • 如有疑问,请返回基本解析技术!
【解决方案2】:

我在 PHP 中使用的一种方法是将嵌套表达式(在本例中为“{x(a,b,c-d)}”)的最深点替换为符号,例如 '¶1' ,然后将其解析值(即 [('x',['a'],['b'],['c-d'])])保存到变量 $nest1 中。

你现在有原始字符串 'a,"b,c,d",{x(a,b,c-d)}' 看起来像 'a,"b,c,d",¶1' 被解析就像前三个一样。然后只需在结果数组中搜索以 '¶' 开头的任何内容,并将其替换为其关联的变量。

此方法支持任意多个级别,只需保持循环/递归,直到所有符号都消失。例如,

'a,"b,c,d",{x(a,b,{y(j,k,l-m)},c-d)}'
'a,"b,c,d",{x(a,b,¶1,c-d)}' and $nest1=[('y',['j'],['k'],['l-m'])]
'a,"b,c,d",¶2' and $nest2=[('x',['a'],['b'],['¶1'],['c-d'])]
['a'],['b','c','d'],['¶2']
['a'],['b','c','d'],[('x',['a'],['b'],['¶1'],['c-d'])]
['a'],['b','c','d'],[('x',['a'],['b'],[('y',['j'],['k'],['l-m'])],['c-d'])]

为了安全起见,您甚至可以在进行更改之前转义字符串中可能出现的任何 ¶ 实例,然后在您认为有必要时将它们作为最后一步取消转义。

我不懂 Python,所以它的工作方式可能与 PHP 不同。您可能需要使用数组而不是动态变量。

【讨论】:

    【解决方案3】:

    字符串中有引号吗?

    如果不是 - 只需替换控制字符以使与 JSON 兼容并使用 JSON 解析器

    【讨论】:

      【解决方案4】:

      对于前三种情况,您可以递归地应用 CSV 阅读器:

      import csv
      
      def expand( st ):
          if "," not in st:
              return st
          return [ expand( col ) for col in csv.reader( [ st ] ).next() ]
      
      print expand( '"a,b,c",d,"e,f"' )
      print expand( '"a,b,c",d,e' )
      print expand( 'a,b,"c,d,e,f"' )
      

      【讨论】:

        猜你喜欢
        • 2013-06-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-28
        • 1970-01-01
        • 2012-01-27
        • 2016-12-06
        • 1970-01-01
        相关资源
        最近更新 更多