2013-02-16 6 views
5

多くのプログラミング言語の標準ライブラリには、テキスト入力ストリームから文字列、数字、またはその他のオブジェクトを抽出するための「スキャナAPI」が含まれています。 (例えば、JavaにはScannerクラスが含まれ、C++にはistreamが含まれ、Cにはscanfが含まれます)。Python Stream Extraction

Pythonでこれと同等のものは何ですか?

Pythonには、ストリームインターフェイス、つまりio.IOBaseを継承するクラスがあります。しかし、Python TextIOBaseストリームインタフェースは、行指向入力のための機能しか提供しません。 reading the documentationsearching on Googleの後、標準のPythonモジュールで、テキストストリームから整数を抽出したり、スペースで区切られた次の単語を文字列として抽出したりするようなものは見つけられません。これを行うための標準的な施設はありますか?

+0

入力が非常に汚い/任意の場合、私は 're'モジュールを使用する傾向があります。入力が構造化されているなら、私はsimpleparse(EBNLは正規表現よりも保守が容易です)のようなパーサーライブラリを好むでしょう。 –

+1

ユースケースを念頭に置いている場合は、一般的なお問い合わせを行う代わりに、質問を更新する方がより生産的になる可能性があります。 –

+0

他の提案についてはhttp://stackoverflow.com/questions/2175080/sscanf-in-pythonを参照してください。 –

答えて

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を自分で処理する必要があります。