2013-06-14 35 views
24

Nếu tôi có 1000 file pdf cần phải được sáp nhập vào một pdf,pypdf Kết hợp nhiều file PDF thành một pdf

input = PdfFileReader() 
output = PdfFileWriter() 
filename0000 ----- filename 1000 
    input = PdfFileReader(file(filename, "rb")) 
    pageCount = input.getNumPages() 
    for iPage in range(0, pageCount): 
     output.addPage(input.getPage(iPage)) 
outputStream = file("document-output.pdf", "wb") 
output.write(outputStream) 
outputStream.close() 

Execute mã trên, khi input = PdfFileReader(file(filename500+, "rb")),

Một thông báo lỗi: IOError: [Errno 24] Too many open files:

tôi nghĩ rằng đây là một lỗi, Nếu không, tôi nên làm gì?

Trả lời

48

Gần đây tôi đi qua cùng một vấn đề này chính xác, vì vậy tôi đào vào PyPDF2 để xem những gì đang xảy ra, và làm thế nào để giải quyết nó.

Lưu ý: Tôi giả định rằng filename là một chuỗi đường dẫn tệp được định dạng tốt. Giả sử như nhau cho tất cả các mã của tôi

The Short trả lời

Sử dụng lớp PdfFileMerger() thay vì lớp PdfFileWriter(). Tôi đã cố gắng để cung cấp những điều sau đây giống như chặt chẽ nội dung của bạn như tôi có thể:

from PyPDF2 import PdfFileMerger, PdfFileReader 

[...] 

merger = PdfFileMerger() 
for filename in filenames: 
    merger.append(PdfFileReader(file(filename, 'rb'))) 

merger.write("document-output.pdf") 

The Long trả lời

Cách bạn đang sử dụng PdfFileReaderPdfFileWriter là giữ mỗi tập tin mở, và cuối cùng khiến Python tạo IOError 24. Để cụ thể hơn, khi bạn thêm một trang vào PdfFileWriter, bạn sẽ thêm tham chiếu đến trang trong PdfFileReader mở (do đó đã xảy ra lỗi IO được ghi chú nếu bạn đóng tệp). Python phát hiện tệp vẫn được tham chiếu và không thực hiện bất kỳ việc thu thập rác/tệp tự động nào mặc dù sử dụng lại tệp xử lý. Chúng vẫn mở cho đến khi PdfFileWriter không còn cần quyền truy cập vào chúng, tại số output.write(outputStream) trong mã của bạn.

Để giải quyết vấn đề này, hãy tạo bản sao trong bộ nhớ của nội dung và cho phép tệp được đóng. Tôi nhận thấy trong cuộc phiêu lưu của tôi thông qua mã PyPDF2 rằng lớp PdfFileMerger() đã có chức năng này, vì vậy thay vì tái phát minh ra bánh xe, tôi đã chọn sử dụng nó thay thế. Tuy nhiên, tôi đã học được rằng cái nhìn ban đầu của tôi tại PdfFileMerger không đủ gần và nó chỉ tạo ra các bản sao trong một số điều kiện nhất định.

nỗ lực ban đầu của tôi trông giống như sau đây, và được kết quả trong cùng một vấn đề IO:

merger = PdfFileMerger() 
for filename in filenames: 
    merger.append(filename) 

merger.write(output_file_path) 

Nhìn vào mã nguồn PyPDF2, chúng ta thấy rằng append() đòi hỏi fileobj để được thông qua, và sau đó sử dụng merge() , chuyển đến trang cuối cùng của nó dưới dạng vị trí tệp mới. merge() nào sau đây với fileobj (trước khi mở nó với PdfFileReader(fileobj):

if type(fileobj) in (str, unicode): 
     fileobj = file(fileobj, 'rb') 
     my_file = True 
    elif type(fileobj) == file: 
     fileobj.seek(0) 
     filecontent = fileobj.read() 
     fileobj = StringIO(filecontent) 
     my_file = True 
    elif type(fileobj) == PdfFileReader: 
     orig_tell = fileobj.stream.tell() 
     fileobj.stream.seek(0) 
     filecontent = StringIO(fileobj.stream.read()) 
     fileobj.stream.seek(orig_tell) 
     fileobj = filecontent 
     my_file = True 

Chúng ta có thể thấy rằng tùy chọn append() không chấp nhận một chuỗi, và khi làm như vậy, giả định đó là một đường dẫn tập tin và tạo ra một đối tượng tập tin tại vị trí đó Kết quả cuối cùng là chính xác những gì chúng tôi đang cố gắng tránh.Một đối tượng PdfFileReader() giữ mở một tệp cho đến khi tệp được ghi cuối cùng!

Tuy nhiên, nếu chúng ta tạo đối tượng tệp của chuỗi đường dẫn tệp hoặc a PdfFileReader(xem Chỉnh sửa 2) đối tượng của chuỗi đường dẫn trước nó được chuyển vào append(), nó sẽ tự động tạo bản sao cho chúng tôi làm đối tượng StringIO, cho phép Python đóng tệp.

Tôi khuyên bạn nên đơn giản hơn merger.append(file(filename, 'rb')), vì những người khác đã báo cáo rằng đối tượng PdfFileReader có thể vẫn mở trong bộ nhớ, ngay cả sau khi gọi writer.close().

Hy vọng điều này giúp!

EDIT: Tôi cho rằng bạn đang sử dụng PyPDF2, chứ không phải PyPDF. Nếu bạn không, tôi khuyên bạn nên chuyển đổi, như PyPDF không còn được duy trì với các tác giả cho phước lành chính thức của mình để Phaseit trong việc phát triển PyPDF2.

Nếu vì lý do nào đó bạn không thể đổi sang PyPDF2 (cấp phép, hạn chế hệ thống, v.v.) hơn PdfFileMerger sẽ không khả dụng cho bạn. Trong tình huống đó, bạn có thể sử dụng lại mã từ chức năng merge của PyPDF2 (được cung cấp ở trên) để tạo một bản sao của tệp dưới dạng đối tượng StringIO và sử dụng nó trong mã của bạn thay cho đối tượng tệp.

EDIT 2: giới thiệu trước của việc sử dụng merger.append(PdfFileReader(file(filename, 'rb'))) về thay đổi dựa trên ý kiến ​​(Cảm ơn @Agostino).

+0

Tôi sẽ thành thật; Tôi chưa đọc câu trả lời dài. Câu trả lời ngắn là tuyệt vời mặc dù. – BeReal82

+1

Tôi nhận thấy rằng tôi không thể xóa một số tệp được thêm vào tạo đối tượng 'PdfFileReader' trung gian với lệnh 'writer.append (PdfFileReader (tệp (tên tệp' rb ')))'. Chúng vẫn bị khóa ngay cả sau khi gọi 'writer.close()'. Các cuộc gọi đơn giản 'merger.append (tập tin (tên tập tin, 'rb'))' dường như không có cùng một vấn đề. – Agostino

+1

Điều này sẽ không xảy ra với vấn đề bộ nhớ nếu các tệp quá lớn không? – Nishant

0

nó có thể chỉ là những gì nó nói, bạn là gì o pening để nhiều tập tin. Bạn có thể sử dụng rõ ràng f=file(filename) ... f.close() trong vòng lặp hoặc sử dụng câu lệnh with. Vì vậy, mỗi tập tin mở được đóng đúng cách.

0

Sự cố là bạn chỉ được phép có một số lượng tệp nhất định được mở tại bất kỳ thời điểm nào. Có nhiều cách để thay đổi điều này (http://docs.python.org/3/library/resource.html#resource.getrlimit), nhưng tôi không nghĩ rằng bạn cần điều này.

gì bạn có thể thử được đóng các tập tin trong vòng lặp for:

input = PdfFileReader() 
output = PdfFileWriter() 
for file in filenames: 
    f = open(file, 'rb') 
    input = PdfFileReader(f) 
    # Some code 
    f.close() 
+2

nếu sử dụng f.close(), exec output.write (outputStream), lỗi IO nhắc. – daydaysay

1

Gói pdfrw đọc từng tệp một lần, vì vậy sẽ không gặp phải vấn đề về quá nhiều tệp đang mở. Here là tập lệnh ghép nối ví dụ.

Phần liên quan - giả inputs là một danh sách các tên tập tin đầu vào, và outfn là một tên tập tin đầu ra:

from pdfrw import PdfReader, PdfWriter 

writer = PdfWriter() 
for inpfn in inputs: 
    writer.addpages(PdfReader(inpfn).pages) 
writer.write(outfn) 

Disclaimer: Tôi là tác giả pdfrw chính.

Các vấn đề liên quan