2009-05-22 12 views
34

vorrei analizzare una stringa come questa:Python, come analizzare le stringhe per assomigliare sys.argv

-o 1 --long "Some long string" 

in questo:

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

o simili.

Questo è diverso da getopt o optparse, che avvia con input analizzato sys.argv (come l'output sopra riportato). C'è un modo standard per farlo? Fondamentalmente, questo è "splitting" pur mantenendo le stringhe citate insieme.

Il mio migliore funzione finora:

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

La mia vera dimenticanza ha rivelato: http://stackoverflow.com/questions/92533, mi ha usato shlex.split. Chiaramente me ne sono dimenticato. –

+0

Se ciò di cui hai realmente bisogno è "elaborare le opzioni" e non solo "analizzare le stringhe sulla riga di comando", potresti prendere in considerazione http://docs.python.org/2/library/argparse.html –

risposta

68

credo che si desidera che il modulo di shlex.

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

Grazie! Sapevo che c'era qualcosa di simile! –

+1

È fantastico, tranne per il fatto che non sembra supportare le stringhe Unicode. Il documento dice che Python 2.7.3 supporta le stringhe Unicode, ma ci sto provando e 'shlex.split (u'abc 123 → ')' mi dà un 'UnicodeEncodeError'. –

+2

Immagino che 'list (a.decode ('utf-8') per a in shlex.split (u'abc 123 → '.encode (' utf-8 ')))' funzionerà. –

0

Prima ero consapevole di shlex.split, ho fatto la seguente:

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

Questo funziona con Python 2.xe 3.x. Su Python 2.x, funziona direttamente con stringhe di byte e stringhe Unicode. Su Python 3.x, solo solo accetta stringhe [Unicode], non bytes oggetti.

Questo non si comporta esattamente come shell argv frazionamento permette anche citare di CR, LF e caratteri TAB come \r, \n e \t, convertendoli in real CR, LF, TAB (shlex.split non fare quella). Quindi scrivere la mia funzione era utile per i miei bisogni. Immagino che lo shlex.split sia meglio se vuoi solo una semplice suddivisione di argv in stile shell. Sto condividendo questo codice nel caso in cui sia utile come base per fare qualcosa di leggermente diverso.

Problemi correlati