Đã có nhiều câu trả lời hay, nhưng nếu toàn bộ tệp của bạn nằm trên một dòng và bạn vẫn muốn xử lý "hàng" (trái ngược với các khối có kích thước cố định), những câu trả lời này sẽ không giúp bạn.
99% thời gian, có thể xử lý các tệp theo từng dòng. Sau đó, như được đề xuất trong câu trả lời này , bạn có thể sử dụng chính đối tượng tệp làm trình tạo lười biếng:
with open('big.csv') as f:
for line in f:
process(line)
Tuy nhiên, tôi đã từng chạy vào một tệp dòng rất lớn (gần như), trong đó thực tế là dấu phân cách hàng '\n'
nhưng không phải '|'
.
- Đọc từng dòng không phải là một lựa chọn, nhưng tôi vẫn cần xử lý từng hàng.
- Chuyển đổi
'|'
sang '\n'
trước khi xử lý cũng không nằm trong câu hỏi này, bởi vì một số trường của csv này có chứa '\n'
(đầu vào người dùng văn bản miễn phí).
- Sử dụng thư viện csv cũng bị loại trừ vì thực tế là, ít nhất là trong các phiên bản đầu của lib, nó được mã hóa cứng để đọc từng dòng đầu vào .
Đối với các loại tình huống này, tôi đã tạo đoạn trích sau:
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
Tôi đã có thể sử dụng nó thành công để giải quyết vấn đề của tôi. Nó đã được thử nghiệm rộng rãi, với các kích cỡ khác nhau.
Bộ thử nghiệm, cho những người muốn thuyết phục bản thân.
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')
chỉ là một con trỏ mà không có bất kỳ tiêu thụ bộ nhớ? (Ý tôi là bộ nhớ tiêu thụ là như nhau bất kể kích thước tệp?) Nó sẽ ảnh hưởng đến hiệu suất như thế nào nếu tôi sử dụng urllib.readline () thay vì f.readline ()?