2009-05-22 12 views
34

me gustaría analizar una cadena como esta:Python, cómo analizar cadenas para parecerse sys.argv

-o 1 --long "Some long string" 

en esto:

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

o similar.

Esto es diferente de getopt, o optparse, que comienza con la entrada analizada sys.argv (como la salida que tengo más arriba). ¿Hay una manera estándar de hacer esto? Básicamente, esto es "dividir" al mantener unidas las cuerdas entrelazadas.

Mi mejor función hasta el momento:

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

Mi verdadero olvido revelado: http://stackoverflow.com/questions/92533, me tiene usando shlex.split. Claramente, simplemente me olvidé de eso. –

+0

Si lo que realmente necesita es "procesar opciones" y no solo "analizar cadenas en la línea de comandos", podría considerar http://docs.python.org/2/library/argparse.html –

Respuesta

68

creo que desea que el módulo de shlex.

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

¡Gracias! ¡Sabía que había algo como esto! –

+1

Eso es genial, excepto que no parece ser compatible con cadenas Unicode. El documento dice que Python 2.7.3 admite cadenas Unicode, pero lo estoy intentando y 'shlex.split (u'abc 123 → ')' me da un 'UnicodeEncodeError'. –

+2

Supongo que 'list (a.decode ('utf-8') para a en shlex.split (u'abc 123 → '.encode (' utf-8 ')))' funcionará. –

0

Antes yo era consciente de shlex.split, hice la siguiente:

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

Esto funciona con Python 2.x y 3.x En Python 2.x, funciona directamente con cadenas de bytes y cadenas Unicode. En Python 3.x, es solo acepta cadenas [Unicode], no bytes objetos.

Esto no se comporta exactamente igual que la cáscara argv división, sino que también permite cita de CR, LF y caracteres de tabulación como \r, \n y \t, convirtiéndolos a bienes CR, LF, TAB (shlex.split no hace ese). Así que escribir mi propia función fue útil para mis necesidades. Supongo que shlex.split es mejor si solo quieres una división de argv estilo shell simple. Estoy compartiendo este código en caso de que sea útil como línea de base para hacer algo ligeramente diferente.

Cuestiones relacionadas