Làm cách nào tôi có thể đọc các tệp văn bản lớn trong Python, từng dòng mà không tải nó vào bộ nhớ?


238

Tôi cần đọc một tập tin lớn, từng dòng một. Hãy nói rằng tập tin có hơn 5GB và tôi cần đọc từng dòng, nhưng rõ ràng tôi không muốn sử dụng readlines()vì nó sẽ tạo ra một danh sách rất lớn trong bộ nhớ.

Làm thế nào mã dưới đây sẽ làm việc cho trường hợp này? Có phải xreadlineschính nó đang đọc từng cái một vào bộ nhớ? Là biểu thức máy phát điện cần thiết?

f = (line for line in open("log.txt").xreadlines())  # how much is loaded in memory?

f.next()  

Ngoài ra, tôi có thể làm gì để đọc thứ này theo thứ tự ngược lại, giống như taillệnh Linux ?

Tôi đã tìm thấy:

http://code.google.com.vn/p/pytailer/

" Đầu trăn, đuôi và đọc ngược bằng các dòng của tệp văn bản "

Cả hai đều làm việc rất tốt!


Và tôi có thể làm gì để đọc cái này từ đuôi? từng dòng, bắt đầu trong dòng cuối cùng.
Bruno Rocha - rochacbruno

đây phải là một câu hỏi riêng biệt
cmcginty

Câu trả lời:


308

Tôi đã cung cấp câu trả lời này bởi vì Keith, trong khi cô đọng, không đóng tệp một cách rõ ràng

with open("log.txt") as infile:
    for line in infile:
        do_something_with(line)

30
câu hỏi vẫn là, "cho dòng trong infile" sẽ tải 5GB dòng của tôi vào bộ nhớ? và, Làm thế nào tôi có thể đọc từ đuôi?
Bruno Rocha - rochacbruno

66
@rochacbruno, nó chỉ đọc một dòng tại một thời điểm. Khi dòng tiếp theo được đọc, dòng trước sẽ là rác được thu thập trừ khi bạn đã lưu trữ một tham chiếu đến nó ở một nơi khác
John La Rooy

1
@rochacbruno, Đọc các dòng theo thứ tự ngược lại không dễ thực hiện một cách hiệu quả thật đáng tiếc. Nói chung, bạn sẽ muốn đọc từ cuối tệp theo các đoạn có kích thước hợp lý (kilobyte đến megabyte nói) và phân chia trên các ký tự dòng mới (hoặc bất cứ dòng char nào kết thúc trên nền tảng của bạn)
John La Rooy

4
Cảm ơn! Tôi tìm thấy giải pháp đuôi stackoverflow.com/questions/5896079/ từ
Bruno Rocha - rochacbruno

1
@bawejakunal, Ý bạn là nếu một dòng quá dài để tải vào bộ nhớ cùng một lúc? Đó là bất thường cho một tập tin văn bản . Thay vì sử dụng forvòng lặp lặp trên các dòng, bạn có thể sử dụng chunk = infile.read(chunksize)để đọc các đoạn có kích thước giới hạn bất kể nội dung của chúng. Bạn sẽ phải tự tìm kiếm bên trong các đoạn để tìm dòng mới.
John La Rooy

60

Tất cả những gì bạn cần làm là sử dụng đối tượng tệp như một trình vòng lặp.

for line in open("log.txt"):
    do_something_with(line)

Thậm chí tốt hơn là sử dụng trình quản lý bối cảnh trong các phiên bản Python gần đây.

with open("log.txt") as fileobject:
    for line in fileobject:
        do_something_with(line)

Điều này sẽ tự động đóng các tập tin là tốt.


2
Đó không phải là tải toàn bộ tập tin vào bộ nhớ?
Bruno Rocha - rochacbruno

17

Một cách tiếp cận trường học cũ:

fh = open(file_name, 'rt')
line = fh.readline()
while line:
    # do stuff with line
    line = fh.readline()
fh.close()

2
lưu ý nhỏ: để đảm bảo an toàn ngoại lệ, bạn nên sử dụng câu lệnh 'with', trong trường hợp của bạn "với open (tên tệp, 'rt') là fh:"
prokher

16
@prokher: Vâng, nhưng tôi đã gọi đây là "trường học cũ".
PTBNL

15

Thay vào đó, bạn nên sử dụng một iterator. Có liên quan: http://docs.python.org/l Library / fileinput.html

Từ các tài liệu:

import fileinput
for line in fileinput.input("filename"):
    process(line)

Điều này sẽ tránh sao chép toàn bộ tập tin vào bộ nhớ cùng một lúc.


Mặc dù các tài liệu hiển thị đoạn mã là "sử dụng thông thường", sử dụng nó không gọi close()phương thức của FileInputđối tượng lớp được trả về khi vòng lặp kết thúc - vì vậy tôi sẽ tránh sử dụng theo cách này. Trong Python 3.2, cuối cùng họ đã fileinputtương thích với giao thức quản lý bối cảnh giải quyết vấn đề này (nhưng mã vẫn không được viết theo cách hiển thị).
martineau

7

Đây là những gì bạn làm nếu bạn không có dòng mới trong tệp:

with open('large_text.txt') as f:
  while True:
    c = f.read(1024)
    if not c:
      break
    print(c)

Trong khi tôi thích phương pháp này, bạn có nguy cơ bị dòng trong văn bản của bạn bị vỡ thành nhiều đoạn. Tôi đã nhìn thấy điều này một cách cá nhân, điều đó có nghĩa là nếu bạn đang tìm kiếm chuỗi trong tệp giống như tôi, tôi sẽ bỏ lỡ một số vì dòng họ đang ở bị vỡ thành nhiều đoạn. Có cách nào để khắc phục điều này? Sử dụng các dòng đọc không hoạt động tốt vì tôi đã nhận được thông tin sai lệch @Ariel Cabib
edo101

6

Vui lòng thử điều này:

with open('filename','r',buffering=100000) as f:
    for line in f:
        print line

vui lòng giải thích?
Nikhil VJ

3
Từ docmunets chính thức của Python: link Đối số bộ đệm tùy chọn chỉ định kích thước bộ đệm mong muốn của tệp: 0 có nghĩa là không có bộ đệm, 1 có nghĩa là bộ đệm dòng, bất kỳ giá trị dương nào khác có nghĩa là sử dụng bộ đệm có kích thước (xấp xỉ) (theo byte). Bộ đệm âm có nghĩa là sử dụng mặc định hệ thống, thường là bộ đệm dòng cho các thiết bị tty và được đệm hoàn toàn cho các tệp khác. Nếu bị bỏ qua, mặc định hệ thống được sử dụng
jyoti das

Lưu lại ngày của tôi, trong trường hợp của tôi, với các tệp> ~ 4gb với hai trình xử lý tệp (một đọc, ghi khác) python đã bị treo và bây giờ thì ổn! Cảm ơn.
Xelt

@jyotidas Trong khi tôi thích phương pháp này, bạn có nguy cơ bị dòng trong văn bản của mình bị vỡ thành nhiều đoạn. Tôi đã nhìn thấy điều này một cách cá nhân, điều đó có nghĩa là nếu bạn đang tìm kiếm chuỗi trong tệp giống như tôi, tôi sẽ bỏ lỡ một số vì dòng họ đang ở bị vỡ thành nhiều đoạn. Có cách nào để khắc phục điều này? Sử dụng các dòng đọc không hoạt động tốt khi tôi gặp phải sự cố
edo101

3

Tôi không thể tin rằng nó có thể dễ như câu trả lời của @ john-la-rooy. Vì vậy, tôi đã tạo lại cplệnh bằng cách đọc và viết từng dòng. Đó là NHANH CHÓNG.

#!/usr/bin/env python3.6

import sys

with open(sys.argv[2], 'w') as outfile:
    with open(sys.argv[1]) as infile:
        for line in infile:
            outfile.write(line)

LƯU Ý: Vì python readlinechuẩn hóa các kết thúc dòng, điều này có tác dụng phụ là chuyển đổi các tài liệu với các kết thúc dòng DOS \r\nthành các kết thúc dòng Unix \n. Toàn bộ lý do của tôi để tìm kiếm chủ đề này là tôi cần phải chuyển đổi một tệp nhật ký nhận được một mớ kết thúc dòng (vì nhà phát triển đã sử dụng một cách mù quáng các thư viện .NET). Tôi đã bị sốc khi thấy rằng sau bài kiểm tra tốc độ ban đầu của mình, tôi không cần phải quay lại và rstripxếp hàng. Nó đã hoàn hảo rồi!
Bruno Bronosky

2

Các ngọn lửa dự án đã đi một chặng đường dài trong 6 năm qua. Nó có một API đơn giản bao gồm một tập hợp con hữu ích của các tính năng gấu trúc.

dask.dataframe đảm nhiệm việc phân chia nội bộ, hỗ trợ nhiều hoạt động song song và cho phép bạn xuất các lát trở lại gấu trúc một cách dễ dàng cho các hoạt động trong bộ nhớ.

import dask.dataframe as dd

df = dd.read_csv('filename.csv')
df.head(10)  # return first 10 rows
df.tail(10)  # return last 10 rows

# iterate rows
for idx, row in df.iterrows():
    ...

# group by my_field and return mean
df.groupby(df.my_field).value.mean().compute()

# slice by column
df[df.my_field=='XYZ'].compute()

2

Đây là mã để tải các tệp văn bản có kích thước bất kỳ mà không gây ra vấn đề bộ nhớ. Nó hỗ trợ các tệp có kích thước gigabyte

https://gist.github.com/iyvinjose/e6c1cb2821abd5f01fd1b9065cbc759d

tải xuống tệp data_loading_utils.py và nhập nó vào mã của bạn

sử dụng

import data_loading_utils.py.py
file_name = 'file_name.ext'
CHUNK_SIZE = 1000000


def process_lines(data, eof, file_name):

    # check if end of file reached
    if not eof:
         # process data, data is one single line of the file

    else:
         # end of file reached

data_loading_utils.read_lines_from_file_as_data_chunks(file_name, chunk_size=CHUNK_SIZE, callback=self.process_lines)

Phương thức process_lines là hàm gọi lại. Nó sẽ được gọi cho tất cả các dòng, với dữ liệu tham số đại diện cho một dòng duy nhất của tệp tại một thời điểm.

Bạn có thể định cấu hình biến CHUNK_SIZE tùy thuộc vào cấu hình phần cứng máy của bạn.


Trong khi tôi thích phương pháp này, bạn có nguy cơ bị dòng trong văn bản của bạn bị vỡ thành nhiều đoạn. Tôi đã nhìn thấy điều này một cách cá nhân, điều đó có nghĩa là nếu bạn đang tìm kiếm chuỗi trong tệp giống như tôi, tôi sẽ bỏ lỡ một số vì dòng họ đang ở bị vỡ thành nhiều đoạn. Có cách nào để khắc phục điều này? Sử dụng các dòng đọc không hoạt động tốt khi tôi gặp phải sự cố
edo101

0

Còn cái này thì sao? Chia tệp của bạn thành các đoạn và sau đó đọc từng dòng một, bởi vì khi bạn đọc một tệp, hệ điều hành của bạn sẽ lưu trữ dòng tiếp theo. Nếu bạn đang đọc từng dòng tệp, bạn không sử dụng hiệu quả thông tin được lưu trong bộ nhớ cache.

Thay vào đó, chia tệp thành các khối và tải toàn bộ khối vào bộ nhớ và sau đó thực hiện xử lý của bạn.

def chunks(file,size=1024):
    while 1:

        startat=fh.tell()
        print startat #file's object current position from the start
        fh.seek(size,1) #offset from current postion -->1
        data=fh.readline()
        yield startat,fh.tell()-startat #doesnt store whole list in memory
        if not data:
            break
if os.path.isfile(fname):
    try:
        fh=open(fname,'rb') 
    except IOError as e: #file --> permission denied
        print "I/O error({0}): {1}".format(e.errno, e.strerror)
    except Exception as e1: #handle other exceptions such as attribute errors
        print "Unexpected error: {0}".format(e1)
    for ele in chunks(fh):
        fh.seek(ele[0])#startat
        data=fh.read(ele[1])#endat
        print data

Điều này có vẻ đầy hứa hẹn. Đây là tải theo byte hoặc theo dòng? Tôi sợ các dòng bị hỏng nếu bởi byte .. làm thế nào chúng ta có thể tải 1000 dòng tại một thời điểm và xử lý điều đó?
Nikhil VJ

0

Cảm ơn bạn! Gần đây tôi đã chuyển đổi sang python 3 và đã bị thất vọng khi sử dụng readlines (0) để đọc các tệp lớn. Điều này đã giải quyết vấn đề. Nhưng để có được mỗi dòng, tôi phải làm thêm một vài bước. Mỗi dòng được đi trước bởi một "b '" mà tôi đoán rằng nó ở định dạng nhị phân. Sử dụng "giải mã (utf-8)" đã thay đổi ascii.

Sau đó, tôi phải xóa một "= \ n" ở giữa mỗi dòng.

Sau đó, tôi chia các dòng ở dòng mới.

b_data=(fh.read(ele[1]))#endat This is one chunk of ascii data in binary format
        a_data=((binascii.b2a_qp(b_data)).decode('utf-8')) #Data chunk in 'split' ascii format
        data_chunk = (a_data.replace('=\n','').strip()) #Splitting characters removed
        data_list = data_chunk.split('\n')  #List containing lines in chunk
        #print(data_list,'\n')
        #time.sleep(1)
        for j in range(len(data_list)): #iterate through data_list to get each item 
            i += 1
            line_of_data = data_list[j]
            print(line_of_data)

Đây là mã bắt đầu ngay phía trên "dữ liệu in" trong mã của Arohi.


0

Tôi đã trình bày một cách tiếp cận truy cập ngẫu nhiên mức byte song song ở đây trong câu hỏi khác này:

Lấy số lượng dòng trong một tệp văn bản mà không cần đọc

Một số câu trả lời đã được cung cấp là tốt đẹp và súc tích. Tôi thích một số trong số họ. Nhưng nó thực sự phụ thuộc vào những gì bạn muốn làm với dữ liệu trong tệp. Trong trường hợp của tôi, tôi chỉ muốn đếm các dòng, càng nhanh càng tốt trên các tệp văn bản lớn. Mã của tôi có thể được sửa đổi để làm những thứ khác tất nhiên, giống như bất kỳ mã nào.


0

Giải pháp tốt nhất tôi tìm thấy liên quan đến vấn đề này và tôi đã thử nó trên tệp 330 MB.

lineno = 500
line_length = 8
with open('catfour.txt', 'r') as file:
    file.seek(lineno * (line_length + 2))
    print(file.readline(), end='')

Trong đó line_length là số lượng ký tự trong một dòng. Ví dụ "abcd" có độ dài dòng 4.

Tôi đã thêm 2 chiều dài dòng để bỏ qua ký tự '\ n' và chuyển sang ký tự tiếp theo.


-1

Điều này có thể hữu ích khi bạn muốn làm việc song song và chỉ đọc các khối dữ liệu nhưng giữ sạch sẽ với các dòng mới.

def readInChunks(fileObj, chunkSize=1024):
    while True:
        data = fileObj.read(chunkSize)
        if not data:
            break
        while data[-1:] != '\n':
            data+=fileObj.read(1)
        yield data

-10
f=open('filename','r').read()
f1=f.split('\n')
for i in range (len(f1)):
    do_something_with(f1[i])

hi vọng điêu nay co ich.


5
Điều này sẽ không đọc toàn bộ tập tin trong bộ nhớ? Câu hỏi hỏi rõ ràng làm thế nào để tránh điều đó, do đó điều này không trả lời câu hỏi.
Nghịch lý Fermi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.