2016-05-14 11 views
2

私は大文字の入力ファイルを持っていますので、私はenumerateまたはfo.readlines()を使いたくありません。 for line in fo:伝統的な方法では動作しませんし、私は理由を述べますが、私はそれが私が今必要なものにいくつかの変更を感じる。次のファイルを考えてみましょう:私は必要なのは、行の変数チャンクを読んタプルの座標をペア、例リストにタプルを追加し、ファイルから新しいケースを読みに戻って移動することができることですPython:ファイルを行単位で読み込む最も最適な方法

input_file.txt: 
3 # No of tests that will follow 
3 # No of points in current test 
1 # 1st x-coordinate 
2 # 2nd x-coordinate 
3 # 3rd x-coordinate 
2 # 1st y-coordinate 
4 # 2nd y-coordinate 
6 # 3rd y-coordinate 
... 

私はこの考え:

with open(input_file) as f: 
    T = int(next(f)) 
    for _ in range(T): 
     N = int(next(f)) 
     for i in range(N): 
      x.append(int(f.next())) 
     for i in range(N): 
      y.append(int(f.next())) 

その後タプルに夫婦二つのリストを。私はこれを行うためのよりクリーンな方法がなければならないと感じています。助言がありますか?

EDIT:y座標にはforループを別に読み込む必要があります。それらはx座標とy座標がn行離れています。だから、行iを読む。読取り線(i + n);各ケースについて、n回繰り返す。

+0

だから、1回の繰り返しでn行を読みたいのですか? – Copperfield

+0

私はあなたの入力フォーマットとデータ構造の望ましい出力ストリームを完全に理解していません。簡単な例を教えてください。 – 5gon12eder

+0

enumerate()で何が問題になっていますか? –

答えて

3

これは可能な限り最短の解決策ではないかもしれませんが、私は「かなり最適」だと思います。

def parse_number(stream): 
    return int(next(stream).partition('#')[0].strip()) 

def parse_coords(stream, count): 
    return [parse_number(stream) for i in range(count)] 

def parse_test(stream): 
    count = parse_number(stream) 
    return list(zip(parse_coords(stream, count), parse_coords(stream, count))) 

def parse_file(stream): 
    for i in range(parse_number(stream)): 
     yield parse_test(stream) 

それは熱心に単一のテストのすべての座標を解析しますが、あなたはそれを求めるよう各テストは唯一のレイジー解析されます。

あなたはテストを反復するために、このようにそれを使用することができます:

if __name__ == '__main__': 
    with open('input.txt') as istr: 
     for test in parse_file(istr): 
      print(test) 

より良い関数名は、より良い怠惰な関数から熱心に区別することが望まれるかもしれません。私は今、創造性の命名の欠如を経験しています。

+0

クリーンでクリアなコードの場合+1。 'zip(parse_coords(stream、count)、parse_coords(stream、count))'このタプルは同じ要素を2回も持たないのですか?私はここで何が欠けているのですか? – Utumbu

+0

私はこれが少しトリッキーだと認めます。 'parse_coords'は熱心に評価されるので、最初の呼び出しはストリームから' count' * x *座標を消費し、2番目の呼び出しは 'count' * y *座標を消費します。 'zip'は既に完全に構​​築された' list'を2つ圧縮します。 'stream'は参照によって渡され、関数によって変更されていることに注意してください。 – 5gon12eder

2

どのようにここにgrouper recipe

from itertools import zip_longest 

def grouper(iterable, n, fillvalue=None): 
    """Collect data into fixed-length chunks or blocks 
     grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx""" 
    args = [iter(iterable)] * n 
    return zip_longest(*args, fillvalue=fillvalue) 

with open(input_file) as archi: 
    T = int(next(archi)) 
    N = int(next(archi)) 
    points = [ g for g in grouper(map(int,archi),N) ] 
    print(points) # [(1, 2, 3), (2, 4, 6)] 
    result = list(zip(*points)) 
    print(result) # [(1, 2), (2, 4), (3, 6)] 

でこのことについて、私は一緒にすべてのそれらをペアリングするジッパーを使用し、その後、すべてのxおよびすべてのyでタプルのリストを取得する時にN行を読み取るためにハタを使用

+0

質問の例ファイルを再度読んで、ケースの数とケースのポイントの数を教えてください。ヒント:OPは同じミスを犯したので、あなたは間違っていると言うことができます。 – gboffi

+0

@gboffiそうです。私はそれを修正しました。 +1グルーパーの驚くべき使用。しかし、私が受け入れていない理由と、私が 'f.next()'を使っていない理由は、私がもっと簡潔でエレガントなものを探しているからです。 – Utumbu

0

"ファイルを1行ずつ読み込む"ようなことはありません。大きなリスト/配列のように扱うが、過度のメモリ消費を引き起こすことなく、ファイルをスキップしたいと思うようだ。

mmapモジュールを見ましたか? .find()のようなメソッドを使用して、オプションでオフセット(現在のテストヘッダーの直前など)から開始して、.seek()などのメソッドを使用して、ファイルポインタを見つけたn番目のアイテムに移動してから.readline()などに移動できます。

オブジェクトは、文字列またはバイト配列のいくつかのメソッドとプロパティを共有し、オブジェクトのようなファイルからいくつかを共有します。したがって、.find()(文字列とバイト配列の場合は標準)と.seek()(ファイルの場合)のようなメソッドを組み合わせて使用​​できます。

さらに、Pythonメモリマッピングでは、オペレーティングシステムの機能を使用してファイルをメモリにマッピングします。 (Linuxなどのシステムでは、これは共有ライブラリが実行中のすべてのプロセスのアドレス空間にマップされるのと同じメカニズムです)。重要なポイントは、メモリはファイルの内容のキャッシュとしてのみ使用され、オペレーティングシステムはファイルの内容でメモリバッファをロードおよび解放するために必要なI/Oを透過的に実行することです。

文字や文字列の「n番目」を見つける方法が表示されないため、行をスキップする方法がありません。限り、.find()をループする必要がありますが、Pythonのスライス表記を使ってそのような行にスキップすることができます。一度に1000行のターミネータをスキャンし、それらをインデックス/リストに格納するユーティリティクラス/オブジェクトを書くことができます。次に、メモリマッピングのスライスの値を使用できます。

関連する問題