7

無視したい行がいくつかある BIG ファイルとfile_function、ファイル オブジェクトを受け取る関数 ( ) があるとします。最初にファイル全体を読み取らずに、行が何らかの条件を満たす新しいファイル オブジェクトを返すことはできますか?この遅延は重要な部分です。

注: これらの行を無視して一時ファイルを保存することもできますが、これは理想的ではありません。

たとえば、csv ファイル (行が悪い) があるとします。

1,2
ooops
3,4

最初の試みは、(ファイルと同じメソッドで) 新しいファイル オブジェクトを作成し、上書きすることでしたreadline:

class FileWithoutCondition(file):
    def __init__(self, f, condition):
        self.f = f
        self.condition = condition
    def readline(self):
        while True:
            x = self.f.readline()
            if self.condition(x):
                return x

これは ...file_nameを使用する場合にのみ機能しreadlineますが、他の機能が必要な場合は機能しません。

with ('file_name', 'r') as f:
    f1 = FileWithoutOoops(f, lambda x: x != 'ooops\n')
    result = file_function(f1)

StringIO を使用したソリューションは機能する可能性がありますが、うまくいかないようです。

理想的にfile_functionは、これはブラックボックス関数であると想定する必要があります。具体的には、ジェネレーターを受け入れるように微調整することはできません (ただし、ジェネレーターを微調整してファイルのようにすることはできますか?)。
この種の汎用ファイルの遅延 (スキム) 読み取りを行う標準的な方法はありますか?

注: この質問の動機となる例は、この pandas questionreadlineですpd.read_csv

4

1 に答える 1

1

既存の Python 機能で map-reduce アプローチを使用します。この例では、 string で始まる行を一致させるために正規表現を使用してGET /indexいますが、請求書に適合する条件であれば何でも使用できます。

import re
from collections import defaultdict

pattern = re.compile(r'GET /index\(.*\).html')

# define FILE appropriately.
# map
# the condition here serves to filter lines that can not match.
matches = (pattern.search(line) for line in file(FILE, "rb") if 'GET' in line)
mapp    = (match.group(1) for match in matches if match)

# now reduce, lazy:
count = defaultdict(int)
for request in mapp:
    count[request] += 1

これにより、ラップトップで数秒で 6 GB を超えるファイルがスキャンされます。ファイルをさらにチャンクに分割し、それらをスレッドまたはプロセスに供給することができます。ファイル全体をマップするメモリがない限り、 Iの使用はmmapお勧めしません (ウィンドウはサポートされていません)。

于 2013-02-26T13:58:38.587 に答える