Вже є багато хороших відповідей, але якщо весь ваш файл знаходиться в одному рядку і ви все ще хочете обробити "рядки" (на відміну від блоків фіксованого розміру), ці відповіді вам не допоможуть.
У 99% часу можна обробляти файли по черзі. Тоді, як пропонується у цій відповіді , ви можете використовувати сам об’єкт файлу як генератор ледачих:
with open('big.csv') as f:
for line in f:
process(line)
Тим НЕ менше, я одного разу натрапив на дуже і дуже великий (майже) файл в одному рядку, де роздільник рядка був насправді не '\n'
тільки '|'
.
- Читання рядка за рядком не було варіантом, але мені все одно потрібно було обробляти його рядок за рядком.
- Перетворення
'|'
до '\n'
обробки також не викликало сумнівів, оскільки деякі поля цього CSV містили '\n'
(введення користувачем вільного тексту).
- Використання бібліотеки csv також було виключено, оскільки той факт, що, принаймні, у ранніх версіях lib, важко кодувати читання вхідних рядків за рядком .
Для таких ситуацій я створив такий фрагмент:
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
Я зміг успішно використовувати його для вирішення своєї проблеми. Він пройшов широкі випробування з різними розмірами шматка.
Тестовий набір для тих, хто хоче переконати себе.
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')
- це лише вказівник без споживання пам'яті? (Я маю на увазі, що споживана пам'ять однакова, незалежно від розміру файлу?) Як це вплине на продуктивність, якщо я буду використовувати urllib.readline () замість f.readline ()?