Istnieje już wiele dobrych odpowiedzi, ale jeśli cały plik znajduje się w jednej linii i nadal chcesz przetwarzać „wiersze” (w przeciwieństwie do bloków o stałym rozmiarze), te odpowiedzi ci nie pomogą.
W 99% przypadków możliwe jest przetwarzanie plików linia po linii. Następnie, zgodnie z sugestią zawartą w tej odpowiedzi , możesz użyć samego obiektu pliku jako leniwego generatora:
with open('big.csv') as f:
for line in f:
process(line)
Jednak kiedyś natknąłem się na bardzo duży (prawie) plik jednowierszowy, w którym separator wierszy był w rzeczywistości nie, '\n'
ale '|'
.
- Czytanie wiersz po wierszu nie było opcją, ale nadal musiałem przetwarzać go wiersz po rzędzie.
- Konwersja
'|'
do '\n'
przed przetwarzaniem również nie wchodziła w rachubę, ponieważ niektóre pola tego '\n'
pliku csv zawierały (dowolny tekst wprowadzany przez użytkownika).
- Korzystanie z biblioteki csv również zostało wykluczone, ponieważ fakt, że przynajmniej we wczesnych wersjach biblioteki lib jest na stałe zakodowany, aby czytać wiersz po wierszu .
W takich sytuacjach utworzyłem następujący fragment:
def rows(f, chunksize=1024, sep='|'):
"""
Read a file where the row separator is '|' lazily.
Usage:
>>> with open('big.csv') as f:
>>> for r in rows(f):
>>> process(row)
"""
curr_row = ''
while True:
chunk = f.read(chunksize)
if chunk == '': # End of file
yield curr_row
break
while True:
i = chunk.find(sep)
if i == -1:
break
yield curr_row + chunk[:i]
curr_row = ''
chunk = chunk[i+1:]
curr_row += chunk
Udało mi się to z powodzeniem wykorzystać do rozwiązania mojego problemu. Został gruntownie przetestowany, z różnymi wielkościami porcji.
Zestaw testowy dla tych, którzy chcą się przekonać.
test_file = 'test_file'
def cleanup(func):
def wrapper(*args, **kwargs):
func(*args, **kwargs)
os.unlink(test_file)
return wrapper
@cleanup
def test_empty(chunksize=1024):
with open(test_file, 'w') as f:
f.write('')
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 1
@cleanup
def test_1_char_2_rows(chunksize=1024):
with open(test_file, 'w') as f:
f.write('|')
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 2
@cleanup
def test_1_char(chunksize=1024):
with open(test_file, 'w') as f:
f.write('a')
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 1
@cleanup
def test_1025_chars_1_row(chunksize=1024):
with open(test_file, 'w') as f:
for i in range(1025):
f.write('a')
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 1
@cleanup
def test_1024_chars_2_rows(chunksize=1024):
with open(test_file, 'w') as f:
for i in range(1023):
f.write('a')
f.write('|')
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 2
@cleanup
def test_1025_chars_1026_rows(chunksize=1024):
with open(test_file, 'w') as f:
for i in range(1025):
f.write('|')
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 1026
@cleanup
def test_2048_chars_2_rows(chunksize=1024):
with open(test_file, 'w') as f:
for i in range(1022):
f.write('a')
f.write('|')
f.write('a')
# -- end of 1st chunk --
for i in range(1024):
f.write('a')
# -- end of 2nd chunk
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 2
@cleanup
def test_2049_chars_2_rows(chunksize=1024):
with open(test_file, 'w') as f:
for i in range(1022):
f.write('a')
f.write('|')
f.write('a')
# -- end of 1st chunk --
for i in range(1024):
f.write('a')
# -- end of 2nd chunk
f.write('a')
with open(test_file) as f:
assert len(list(rows(f, chunksize=chunksize))) == 2
if __name__ == '__main__':
for chunksize in [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]:
test_empty(chunksize)
test_1_char_2_rows(chunksize)
test_1_char(chunksize)
test_1025_chars_1_row(chunksize)
test_1024_chars_2_rows(chunksize)
test_1025_chars_1026_rows(chunksize)
test_2048_chars_2_rows(chunksize)
test_2049_chars_2_rows(chunksize)
f = open('really_big_file.dat')
jest tylko wskaźnikiem bez zużycia pamięci? (Mam na myśli, że zużyta pamięć jest taka sama bez względu na rozmiar pliku?) Jak wpłynie to na wydajność, jeśli użyję urllib.readline () zamiast f.readline ()?