将具有三级定界的字符串解析为字典

时间:2012-12-06 21:52:11

标签: python parsing dictionary delimited

我已经找到了如何将分隔的字符串拆分为字典elsewhere中的键:值对,但我有一个传入的字符串,其中还包含两个参数,这些参数相当于字典本身:带有一个或三个键的参数:里面的价值对:

clientid=b59694bf-c7c1-4a3a-8cd5-6dad69f4abb0&keyid=987654321&userdata=ip:192.168.10.10,deviceid:1234,optdata:75BCD15&md=AMT-Cam:avatar&playbackmode=st&ver=6&sessionid=&mk=PC&junketid=1342177342&version=6.7.8.9012

显然,这些是用于混淆专有代码的虚拟参数。我想将所有这些转储到字典中,userdatamd键'的值本身就是字典:

requestdict {'clientid' : 'b59694bf-c7c1-4a3a-8cd5-6dad69f4abb0', 'keyid' : '987654321', 'userdata' : {'ip' : '192.168.10.10', 'deviceid' : '1234', 'optdata' : '75BCD15'}, 'md' : {'Cam' : 'avatar'}, 'playbackmode' : 'st', 'ver' : '6', 'sessionid' : '', 'mk' : 'PC', 'junketid' : '1342177342', 'version' : '6.7.8.9012'}

我可以使用我发现的灵活的两级分隔解析命令:

requestDict = dict(line.split('=') for line in clientRequest.split('&'))

并添加第三个级别来处理&保留二级词典?语法是什么?如果没有,我想我必须按&拆分,然后检查&处理包含:的拆分,但即便如此,我也无法弄清楚语法。有人可以帮忙吗?谢谢!

3 个答案:

答案 0 :(得分:2)

我基本上采用Kyle's answer并使其更适合未来:

def dictelem(input):   
    parts   = input.split('&')
    listing = [part.split('=') for part in parts]

    result = {}
    for entry in listing:
        head, tail = entry[0], ''.join(entry[1:])
        if ':' in tail:
            entries = tail.split(',')
            result.update({ head : dict(e.split(':') for e in entries) })
        else:
            result.update({head: tail})

    return result

答案 1 :(得分:1)

  

我可以使用我发现的灵活的两级分隔解析命令:

requestDict = dict(line.split('=') for line in clientRequest.split('&'))
  

并添加第三个级别来处理&保留二级词典?

当然你可以,但是(a)你可能不想这样,因为超出两个层次的嵌套理解往往变得难以理解,而且(b)这种超级简单的语法对于像你这样的情况不适用只有部分数据可以变成字典。

例如,'PC'会发生什么?你想把它变成{'PC': None}吗?或者是set {'PC'}?还是list ['PC']?或者只是不管它?你必须决定并为此编写逻辑,并尝试将其作为表达式编写,这将使你的决定很难阅读。

所以,让我们把这个逻辑放在一个单独的函数中:

def parseCommasAndColons(s):
    bits = [bit.split(':') for bit in s.split(',')]
    try:
        return dict(bits)
    except ValueError:
        return bits

如果每个以逗号分隔的组件中都包含冒号,但dict类似{'ip': '192.168.10.10', 'deviceid': '1234', 'optdata': '75BCD15'},则会返回{'AMT-Cam': 'avatar'} list['1342177342']。对于其中任何一个没有的情况。

即使这可能有点过于聪明;我可能会使“这是字典格式”检查更明确,而不是只是尝试转换列表列表,看看会发生什么。

无论哪种方式,你如何将它重新置于原始理解中?

好吧,你想在line.split('=')的值上调用它。所以让我们为它添加一个函数:

def parseCommasAndColonsForValue(keyvalue):
    if len(keyvalue) == 2:
        return keyvalue[0], parseCommasAndColons(keyvalue[1])
    else:
        return keyvalue

requestDict = dict(parseCommasAndColonsForValue(line.split('=')) 
                   for line in clientRequest.split('&'))

最后一件事:除非你需要在旧版本的Python上运行,否则你不应该经常在生成器表达式上调用dict。如果它可以被重写为字典理解,那几乎肯定会更清楚,如果它不能被重写为字典理解,那么它可能不应该是一线表达式。

当然,将表达式分解为单独的表达式,将其中的一些转换为语句甚至函数,并命名它们确实会使代码更长 - 但这并不一定意味着更糟。大约一半的Python(import this)致力于解释原因。或者引用Guido的一句话:“Python故意代码打高尔夫球。”

如果你真的想知道它会是什么样子,让我们把它分成两步:

>>> {k: [bit2.split(':') for bit2 in v.split(',')] for k, v in (bit.split('=') for bit in s.split('&'))}
{'clientid': [['b59694bf-c7c1-4a3a-8cd5-6dad69f4abb0']],
 'junketid': [['1342177342']],
 'keyid': [['987654321']],
 'md': [['AMT-Cam', 'avatar']],
 'mk': [['PC']],
 'playbackmode': [['st']],
 'sessionid': [['']],
 'userdata': [['ip', '192.168.10.10'],
              ['deviceid', '1234'],
              ['optdata', '75BCD15']],
 'ver': [['6']],
 'version': [['6.7.8.9012']]}

这说明了为什么你不能只为内部级别添加dict调用 - 因为大部分内容实际上并不是字典,因为它们没有冒号。如果你改变了,那就是这样:

{k: dict(bit2.split(':') for bit2 in v.split(',')) for k, v in (bit.split('=') for bit in s.split('&'))}

我认为这不是非常易读,我怀疑大多数Python程序员会这么做。从现在起6个月阅读并试图弄清楚我的意思比写它要花费更多的精力。

试图调试它并不好玩。如果你在输入上运行那个冒号缺少会发生什么? ValueError: dictionary update sequence element #0 has length 1; 2 is required。哪个序列?不知道。你必须一步一步地分解它,看看哪些不起作用。这没什么好玩的。

所以,希望这说明了你不想这样做的原因。

答案 2 :(得分:1)

这是一个双线程,可以完成我想要的任务:

dictelem = lambda x: x if ':' not in x[1] else [x[0],dict(y.split(':') for y in x[1].split(','))]
a = dict(dictelem(x.split('=')) for x in input.split('&'))