2

我想要一种紧凑的方法来解析以强制性列表元素(未指定的数字)开头并以类似字典的定义结尾的单行字符串=

  • 元素分隔符应该是,空格应该成为元素的一部分——我认为这排除了shlex
  • 空格应该/可以在开头和结尾被剥离(引号也是)
  • 如果一个元素将包含一个,用户需要引用"
    • 任何一个"key=value,with,comma"
    • 或者key="value,with,comma"——更容易实现的
  • 引用错误或包含引号字符的元素有未定义的行为是可以的。
  • 双键的行为也是未定义的。
  • 如果它大大简化了实现,则可以稍微改变一下。

让我们调用函数opts并让它返回 alist和 a dict

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

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!' }

我忽略了shlex,和 argparse,我看不出我应该如何处理这些。不过,我不确定正则表达式是否能解决这个问题。我认为语法有点太严格了。照原样,如果我更喜欢一点(因为它解析 python ;-))optparseconfigparserjsoneval

我的手动解决方案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...
4

3 回答 3

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

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

于 2013-08-11T16:21:54.540 回答
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'})
于 2013-08-11T16:22:54.937 回答
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('')
于 2013-08-11T19:18:20.147 回答