2013-02-16 42 views
5

許多編程語言的標準庫包括一個「掃描儀API」,用於從文本輸入流中提取字符串,數字或其他對象。 (例如,Java包括Scanner類,C++包括istream,並且C包括scanf)。Python流提取

這在Python中等價於什麼?

Python有一個流接口,即從io.IOBase繼承的類。但是,Python接口僅提供面向行輸入的工具。在reading the documentationsearching on Google之後,我找不到標準Python模塊中的某些內容,例如,它允許我從文本流中提取整數,或者將下一個空格分隔的單詞作爲字符串提取。有沒有標準的設施可以做到這一點?

+0

如果輸入非常髒/任意,我傾向於使用're'模塊;如果輸入是結構化的,我更喜歡像simpleparse這樣的解析器庫(EBNL比正則表達式更容易維護)。 – 2013-02-16 01:25:57

+1

如果您確實有一個用例,那麼更新您的問題而不是進行通用查詢可能會更有成效。 – 2013-02-16 01:34:12

+0

有關其他建議,請參見http://stackoverflow.com/questions/2175080/sscanf-in-python。 – 2013-02-16 01:55:37

回答

3

沒有等效的fscanf或Java的Scanner。最簡單的解決方案是要求用戶使用換行分隔輸入而不是空格分隔輸入,然後可以逐行讀取並將行轉換爲正確的類型。

如果您希望用戶提供更多結構化的輸入,那麼您可能應該爲用戶輸入創建解析器。 python有一些很好的解析庫,例如pyparsing。即使最後一次更新是2008年,也有一個scanf模塊。

如果您不想有外部依賴關係,那麼您可以使用正則表達式來匹配輸入序列。當然,正則表達式需要使用字符串,但是您可以輕鬆地克服塊中讀取的限制。例如這樣的事情應該工作以及大部分的時間:

import re 


FORMATS_TYPES = { 
    'd': int, 
    'f': float, 
    's': str, 
} 


FORMATS_REGEXES = {  
    'd': re.compile(r'(?:\s|\b)*([+-]?\d+)(?:\s|\b)*'), 
    'f': re.compile(r'(?:\s|\b)*([+-]?\d+\.?\d*)(?:\s|\b)*'), 
    's': re.compile(r'\b(\w+)\b'), 
} 


FORMAT_FIELD_REGEX = re.compile(r'%(s|d|f)') 


def scan_input(format_string, stream, max_size=float('+inf'), chunk_size=1024): 
    """Scan an input stream and retrieve formatted input.""" 

    chunk = '' 
    format_fields = format_string.split()[::-1] 
    while format_fields: 
     fields = FORMAT_FIELD_REGEX.findall(format_fields.pop()) 
     if not chunk: 
      chunk = _get_chunk(stream, chunk_size) 

     for field in fields: 
      field_regex = FORMATS_REGEXES[field] 
      match = field_regex.search(chunk) 
      length_before = len(chunk) 
      while match is None or match.end() >= len(chunk): 
       chunk += _get_chunk(stream, chunk_size) 
       if not chunk or length_before == len(chunk): 
        if match is None: 
         raise ValueError('Missing fields.') 
        break 
      text = match.group(1) 
      yield FORMATS_TYPES[field](text) 
      chunk = chunk[match.end():] 



def _get_chunk(stream, chunk_size): 
    try: 
     return stream.read(chunk_size) 
    except EOFError: 
     return '' 

用法示例:

>>> s = StringIO('1234 Hello World -13.48 -678 12.45') 
>>> for data in scan_input('%d %s %s %f %d %f', s): print repr(data) 
...                        
1234                       
'Hello' 
'World' 
-13.48 
-678 
12.45 

你可能會不得不延長這一點,並進行適當的測試,但它應該給你一些想法。

1

沒有直接的等價物(據我所知)。但是,您可以使用正則表達式完成相同的操作(請參閱re模塊)。

例如:

# matching first integer (space delimited) 
re.match(r'\b(\d+)\b',string) 

# matching first space delimited word 
re.match(r'\b(\w+)\b',string) 

# matching a word followed by an integer (space delimited) 
re.match(r'\b(\w+)\s+(\d+)\b',string) 

它需要比普通C風格的掃描儀接口多一點的工作,但它也非常靈活和強大。儘管如此,你將不得不自己處理流I/O。