2009-05-22 15 views
34

Tôi muốn phân tích một chuỗi như thế này:Python, làm thế nào để phân tích chuỗi để trông giống như sys.argv

-o 1 --long "Some long string" 

vào đây:

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

hoặc tương đương.

Điều này khác với getopt hoặc optparse, trong đó bắt đầu với đầu vào phân tích cú pháp sys.argv (như đầu ra tôi có ở trên). Có cách nào tiêu chuẩn để làm điều này không? Về cơ bản, đây là "tách" trong khi vẫn giữ các chuỗi được trích dẫn cùng nhau.

chức năng tốt nhất của tôi cho đến nay:

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

Sự quên lãng thực sự của riêng tôi được tiết lộ: http://stackoverflow.com/questions/92533, đã cho tôi sử dụng shlex.split. Rõ ràng là tôi đã quên mất nó. –

+0

Nếu những gì bạn thực sự cần là "để xử lý các tùy chọn" và không chỉ "để phân tích chuỗi trên dòng lệnh", bạn có thể xem xét http://docs.python.org/2/library/argparse.html –

Trả lời

68

Tôi tin rằng bạn muốn module shlex.

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

Cảm ơn bạn! Tôi biết có một cái gì đó như thế này! –

+1

Thật tuyệt vời, ngoại trừ việc nó dường như không hỗ trợ chuỗi Unicode. Tài liệu nói rằng Python 2.7.3 hỗ trợ chuỗi Unicode, nhưng tôi đang thử nó và 'shlex.split (u'abc 123 → ')' cho tôi một 'UnicodeEncodeError'. –

+2

Tôi đoán 'danh sách (a.decode ('utf-8') cho một trong shlex.split (u'abc 123 → '.encode (' utf-8 ')))' sẽ hoạt động. –

0

Trước khi tôi đã nhận thức được shlex.split, tôi đã thực hiện như sau:

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

này làm việc với Python 2.x và 3.x. Trên Python 2.x, nó hoạt động trực tiếp với chuỗi byte và chuỗi Unicode. Trên Python 3.x, chỉ chỉ chấp nhận các chuỗi [Unicode], không phải bytes đối tượng.

này không cư xử giống hệt như vỏ argv tách-nó cũng cho phép trích dẫn của CR, LF và ký tự TAB như \r, \n\t, chuyển đổi chúng sang thực CR, LF, TAB (shlex.split không làm cái đó). Vì vậy, viết chức năng của riêng tôi là hữu ích cho nhu cầu của tôi. Tôi đoán shlex.split là tốt hơn nếu bạn chỉ muốn tách vỏ đồng bằng kiểu argv. Tôi đang chia sẻ mã này trong trường hợp nó hữu ích như là một đường cơ sở để làm một cái gì đó hơi khác nhau.