2009-05-22 15 views
34

Ich möchte eine Zeichenfolge wie folgt analysieren:Python, wie Zeichenfolgen analysieren wie sys.argv aussehen

-o 1 --long "Some long string" 

in diese:

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

oder ähnliches.

Das ist anders als entweder getopt oder optparse, die starten mit sys.argv geparsten Eingang (wie die Ausgabe, die ich oben habe). Gibt es einen Standard Weg dies zu tun? Im Grunde ist dies "Teilen", während in Anführungszeichen gesetzte Strings beibehalten werden.

Meine beste Funktion bisher:

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] 
+0

Meine eigene wahre Vergesslichkeit aufgedeckt: http://stackoverflow.com/questions/92533, hat mich mit shlex.split. Klar habe ich es einfach vergessen. –

+0

Wenn das, was Sie wirklich brauchen, ist "Optionen zu verarbeiten" und nicht nur "Strings auf Kommandozeile zu analysieren", könnten Sie http://docs.python.org/2/library/argparse.html –

Antwort

68

Ich glaube, Sie das shlex Modul wollen.

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

Vielen Dank! Ich wusste, dass es so etwas gab! –

+1

Das ist großartig, außer dass es Unicode-Strings nicht unterstützt. Das Dokument sagt, dass Python 2.7.3 Unicode Strings unterstützt, aber ich versuche es und 'shlex.split (u'abc 123 → ')' gibt mir einen' UnicodeEncodeError'. –

+2

Ich denke '' liste (a.decode ('utf-8') für eine in shlex.split (u'abc 123 → '.encode (' utf-8 '))) wird funktionieren. –

0

Bevor ich bewusst shlex.split war, machte ich folgendes:

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)) 

Dies funktioniert mit Python 2.x und 3.x. In Python 2.x funktioniert es direkt mit Byte-Strings und Unicode-Strings. In Python 3.x akzeptiert es nur akzeptiert [Unicode] Zeichenfolgen, nicht bytes Objekte.

Dies verhält sich nicht genau die gleiche wie Shell argv Aufspaltung erlaubt es auch unter Angabe von CR, LF und TAB-Zeichen als \r, \n und \t, sie zu echten CR, LF, TAB (shlex.split nicht tun Umwandlung Das). Also war es für meine Bedürfnisse nützlich, meine eigene Funktion zu schreiben. Ich denke, shlex.split ist besser, wenn Sie nur einfache Shell-Stil argv splitting wollen. Ich teile diesen Code für den Fall, dass er nützlich ist, um etwas etwas anderes zu tun.