web-dev-qa-db-ja.com

Python、sys.argvのように文字列を解析する方法

次のような文字列を解析したいと思います。

-o 1  --long "Some long string"  

これに:

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

または類似。

これは、(上記の出力のように)sys.argvで解析された入力でstartするgetoptまたはoptparseとは異なります。これを行うための標準的な方法はありますか?基本的に、これは引用符で囲まれた文字列を一緒に保ちながら「分割」します。

これまでの私の最高の機能:

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]
40
Gregg Lind

shlex モジュールが必要だと思います。

>>> import shlex
>>> shlex.split('-o 1 --long "Some long string"')
['-o', '1', '--long', 'Some long string']
80

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文字列で直接機能します。OnPython 3.x、itonlyは、bytesオブジェクトではなく、[Unicode]文字列を受け入れます。

これは、シェルargv分割とまったく同じようには動作しません。また、CR、LF、およびTAB文字を\r\n、および\tとして引用し、実際のCR、LF、TAB( shlex.splitはそれを行いません)。したがって、独自の関数を作成することは私のニーズに役立ちました。単純なシェルスタイルのargv分割が必要な場合は、shlex.splitの方が良いと思います。このコードは、実行のベースラインとして役立つ場合に備えて共有しています。少し違うもの。

3
Craig McQueen