如何将可选和命名参数解析为list和dict?

时间:2013-08-11 15:24:02

标签: python parsing

我想要一种简洁的方法来解析以强制列表元素(未指定的数字)开头的单行字符串,并使用=以字典式定义结束。

  • 我认为元素分隔符应该是,空格应该成为元素的一部分 - 排除shlex
  • 空格应该/可能会在开始和结束时被剥离(也是引号)
  • 如果元素包含,,则用户需要引用"
    • "key=value,with,comma"
    • key="value,with,comma" - 任何更容易实施的内容
  • 使用错误引用或包含quote-char的元素可以使用未定义的行为。
  • 双键的行为也未定义。
  • 如果它可以简化实施,那么这种情况稍有不同。

让我们调用函数opts并让它返回listdict

以下是一些输入示例和所需结果:

opts('dog,cat')                 # -> ["dog", "cat"], {}
opts('big fish,cat')            # -> ["big fish", "cat"], {}
opts('"a dog, a cat",a fish')   # -> ["a dog, a cat", "a fish"], {}
opts('key=value')               # -> [] {'key':'value'}
opts('key=the value,x=y')       # -> [] {'key':'the value', 'x':'y'}
opts('dog, big fish, eats="any, but peas", flies = no! '
   # -> ['dog','big fish'], {'eats':'any, but peas', 'flies':'no!' }

我忽略了shlexargparseoptparseconfigparser,我无法看到我应该如何处理这些问题。不过,我不确定正则表达式是否会破解这个问题。我认为json对语法有点过于严格。和eval一样,如果我更喜欢(因为它解析python; - ))

我在macro中的手动解决方案不是很灵活,我希望将其参数处理替换为上述更一般的opts(s)函数:

def macro(s):
    kw = { 'see':u"\\see", 'type':u"Chapter", 'title': u'??' }
    params = s.split(",")
    kw['label'] = params[0]
    if len(params) > 1:                   # very inflexible
        kw['title'] = params[1]
    for param in params[2:]:              # wrong if p[1] is already key=value
        key, value = param.split("=",1)  # doesn't handle anything, too simple
        kw[key] = value
    # ...rest of code...

目标是在这里使用可重用的函数opts

def macro_see(s):
    ls, kw = opts(s)
    # ...rest of code...

3 个答案:

答案 0 :(得分:1)

您可能想要的是创建自己的分割功能,并在“引入”时切换标记。这样的事情:

def my_split(string, deli):
    res = []
    flag = True
    start = 0

    for i, c in enumerate(string):
        if c == '"':
            if flag:
                flag = False
            else:
                flag = True

        if c == deli and flag:
            res.append(string[start:i])
            start = i+1

    res.append(string[start:])
    return res

从那里开始,这很容易:

def opts(s):
    items = map(lambda x: x.strip(), my_split(s, ','))

    # collect
    ls = []
    kw = {}
    for item in items:
        if '=' in item:
            k, v = item.split('=', 1)
            kw[k.strip()] = v.strip()
        else:
            ls.append(item)

    return ls, kw

这并不完美,你可能还需要做一些事情,但这绝对是一个开始。

答案 1 :(得分:1)

这是一种方法,我按下输入,使其符合python函数参数的语法要求,然后通过eval利用python解释器来解析它们。

import re
s = 'hog, "cog" , dog, bog, "big fish", eats="any, but peas", flies = "no!" '

# I think this will add quotes around any unquoted positional arguments
s = re.sub('(^|,)\ *([^\"\',\ ]+)\ *(?=,|$)', r'\1"\2"', s)

def f(*args, **kwargs):
    return (args, kwargs)

print eval("f("+s+")", {'f':f})

输出:

(('hog', 'cog', 'dog', 'bog', 'big fish'), {'flies': 'no!', 'eats': 'any, but peas'})

答案 2 :(得分:1)

在此解决方案中,opts基本上与yuvi相同(添加了strip)。分割器是shlex的自定义,使用posix模式处理引号。

def mylex(x):
    lex = shlex.shlex(x, posix=True)
    lex.whitespace = ','
    lex.whitespace_split = True
    return list(lex)

def opts(x):
    ll = []
    dd = {}
    items = mylex(x)
    for item in items:
        if '=' in item:
            k, v = item.split('=',1)
            dd[k.strip(' "')] = v.strip(' "')
        else:
            ll.append(item.strip(' "'))
    return (ll,dd)

通过:

trials = [
    ['dog,cat',(["dog", "cat"], {})],
    ['big fish,cat',(["big fish", "cat"], {})],
    ['"a dog, a cat",a fish',(["a dog, a cat", "a fish"], {})],
    ['key=value',([], {'key':'value'})],
    ['key=the value,x=y',([], {'key':'the value', 'x':'y'})],
    ['dog, big fish, eats="any, but peas", flies = no!',(['dog','big fish'], {'eats':'any, but peas', 'flies':'no!' })],
]
for (x,y) in trials:
    print('%r'%x)
    args = opts(x)
    print(args)
    if args != y:
        print('error, %r'%y)
    print('')