47

我想解析这样的字符串:

-o 1  --long "Some long string"  

进入这个:

["-o", "1", "--long", 'Some long string']

或类似的。

这与 getopt 或 optparse 不同,后者以 sys.argv 解析输入开头(就像我上面的输出一样)。有没有标准的方法来做到这一点?基本上,这是“拆分”,同时将引用的字符串保持在一起。

到目前为止我最好的功能:

import csv
def split_quote(string,quotechar='"'):
    '''

    >>> split_quote('--blah "Some argument" here')
    ['--blah', 'Some argument', 'here']

    >>> split_quote("--blah 'Some argument' here", quotechar="'")
    ['--blah', 'Some argument', 'here']
    '''
    s = csv.StringIO(string)
    C = csv.reader(s, delimiter=" ",quotechar=quotechar)
    return list(C)[0]
4

2 回答 2

92

我相信你想要shlex模块。

>>> import shlex
>>> shlex.split('-o 1 --long "Some long string"')
['-o', '1', '--long', 'Some long string']
于 2009-05-22T18:33:07.887 回答
3

在我意识到之前shlex.split,我做了以下事情:

import sys

_WORD_DIVIDERS = set((' ', '\t', '\r', '\n'))

_QUOTE_CHARS_DICT = {
    '\\':   '\\',
    ' ':    ' ',
    '"':    '"',
    'r':    '\r',
    'n':    '\n',
    't':    '\t',
}

def _raise_type_error():
    raise TypeError("Bytes must be decoded to Unicode first")

def parse_to_argv_gen(instring):
    is_in_quotes = False
    instring_iter = iter(instring)
    join_string = instring[0:0]

    c_list = []
    c = ' '
    while True:
        # Skip whitespace
        try:
            while True:
                if not isinstance(c, str) and sys.version_info[0] >= 3:
                    _raise_type_error()
                if c not in _WORD_DIVIDERS:
                    break
                c = next(instring_iter)
        except StopIteration:
            break
        # Read word
        try:
            while True:
                if not isinstance(c, str) and sys.version_info[0] >= 3:
                    _raise_type_error()
                if not is_in_quotes and c in _WORD_DIVIDERS:
                    break
                if c == '"':
                    is_in_quotes = not is_in_quotes
                    c = None
                elif c == '\\':
                    c = next(instring_iter)
                    c = _QUOTE_CHARS_DICT.get(c)
                if c is not None:
                    c_list.append(c)
                c = next(instring_iter)
            yield join_string.join(c_list)
            c_list = []
        except StopIteration:
            yield join_string.join(c_list)
            break

def parse_to_argv(instring):
    return list(parse_to_argv_gen(instring))

这适用于 Python 2.x 和 3.x。在 Python 2.x 上,它直接处理字节字符串和 Unicode 字符串。在 Python 3.x 上,它只接受 [Unicode] 字符串,而不接受bytes对象。

这与 shell argv 拆分的行为并不完全相同——它还允许将 CR、LF 和 TAB 字符引用为\r,\n\t,并将它们转换为真正的 CR、LF、TAB (shlex.split不这样做)。所以编写我自己的函数对我的需求很有用。我想shlex.split如果你只想要简单的 shell 样式的 argv 拆分会更好。我正在分享这段代码,以防它作为基线来做一些稍微不同的事情。

于 2013-05-13T22:55:23.003 回答