2013-02-26 11 views
5

Załóżmy, że mam plik BIG z kilkoma liniami, które chcę zignorować, oraz funkcję (file_function), która pobiera obiekt pliku. Czy mogę zwrócić nowy obiekt pliku, którego linie spełniają pewien warunek bez czytania całego pliku najpierw, to lenistwo jest ważną częścią.Lazyly filtrowanie pliku przed odczytaniem

Uwaga: Mogę zapisać plik tymczasowy z pominięciem tych linii, ale nie jest to idealne rozwiązanie.

Na przykład, załóżmy, że miałem pliku csv (ze złej linii):

1,2 
ooops 
3,4 

Pierwszą próbą było stworzenie nowego obiektu pliku (z tymi samymi metodami jak w pliku) i nadpisania 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 

Działa, jeśli file_name używa tylko readline ... ale nie, jeśli wymaga innej funkcjonalności.

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

Roztwór stosując StringIO mogą działać, ale nie wydaje się uzyskać go.

Idealnie powinniśmy założyć, że file_function jest funkcją blackbox, w szczególności nie mogę po prostu zmienić jej ustawień, aby zaakceptować generator (ale może uda mi się zmodyfikować generator tak, aby był podobny do pliku?).
Czy istnieje standardowy sposób wykonywania tego rodzaju leniwego (skim-) odczytu ogólnego pliku?

Uwaga: motywowanie przykładem na to pytanie jest this pandas question, gdzie tylko o readline nie wystarczy, aby dostać pracę pd.read_csv ...

+2

Myślę, że trzeba dokładnie wyjaśnić nam, jakie wymagania mają obiekty "plik". Jeśli potrzebuje czegoś więcej niż tylko zdolność do generowania linii, musimy wiedzieć, co jeszcze. –

+0

@JohnZwinck Myślę, że możesz mieć rację, miałem nadzieję, że może istnieć jakiś ogólny sposób na zrobienie tego (który działałby z jakimkolwiek plikiem "może być naiwny ..." –

+1

Być może. :) Sprawdź http: // stackoverflow.com/questions/5335017/what-is-the-minimal-subset-of-file-methods-i-need-to-implement-to-get-the-full-p, jeśli jeszcze tego nie zrobiłeś. –

Odpowiedz

1

Użyj map-zmniejszenie podejście z istniejących obiektów Pythona. W tym przykładzie używam wyrażenia regularnego dla dopasowania linii, które zaczynają się od ciągu GET /index, ale można używać niezależnie od stanu pasuje do Twojego Bill:

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 

ten skanuje> plik 6GB w ciągu kilku sekund na moim laptopie . Możesz dalej dzielić plik w porcje i podawać je do wątków lub procesów. Używanie mmap Nie polecam, chyba że masz pamięć do mapowania całego pliku (nie obsługuje okienkowania).

+0

Czy to nie generator, a nie plik? Podobny do odpowiedzi (usunięty) poniżej? –

+0

Czy to jest problem? Możesz owijać generator 'mapp = ...' w swojej klasie podobnej do pliku ''readline' i wydaj go, ale nie zrobiłbym tego, ponieważ nie ma abstrakcyjnej klasy dla pliku, więc musisz wdrożyć wszystkie metody . –

+0

Być może problemem jest brak klasy abstrakcyjnej dla pliku, miałem nadzieję, że uda mi się udawać, że to plik. [Przykład motywowania] (http://stackoverflow.com/questions/15088190/what-is-the-easiest-way-to-load-a-filtered-tda-file-using-pandas) ma postać 'file_function = pd. read_csv' (który wymaga więcej niż readline). :( –

Powiązane problemy