如果我有1000多个pdf文件需要合并为一个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()
执行上述代码,input = PdfFileReader(file(filename500+, "rb"))
,
错误讯息:
IOError: [Errno 24] Too many open files:
我认为这是一个错误,如果没有,我该怎么办?
答案 0 :(得分:57)
我最近遇到了同样的问题,所以我挖到了PyPDF2,看看发生了什么,以及如何解决它。
注意:我假设filename
是格式正确的文件路径字符串。我的所有代码都假设相同
简答
使用PdfFileMerger()
类而不是PdfFileWriter()
类。我试图提供以下内容,尽可能与您的内容非常相似:
from PyPDF2 import PdfFileMerger, PdfFileReader
[...]
merger = PdfFileMerger()
for filename in filenames:
merger.append(PdfFileReader(file(filename, 'rb')))
merger.write("document-output.pdf")
长答案
您使用PdfFileReader
和PdfFileWriter
的方式是保持每个文件都处于打开状态,并最终导致Python生成IOError 24.更具体地说,当您向{{1}添加页面时您正在打开PdfFileWriter
页面中的页面引用(因此,如果您关闭文件,则会出现标注的IO错误)。 Python检测到仍然被引用的文件,尽管重新使用文件句柄,但不会执行任何垃圾收集/自动文件关闭。它们保持打开状态,直到PdfFileReader
不再需要访问它们,代码位于PdfFileWriter
。
要解决此问题,请在内容的内存中创建副本,并允许关闭文件。我在冒险中通过PyPDF2代码注意到output.write(outputStream)
类已经具有此功能,因此我选择使用它而不是重新发明轮子。不过,我了解到,我对PdfFileMerger()
的初步看法并不够近,并且只在某些条件下创建了副本。
我的初始尝试看起来如下,并导致相同的IO问题:
PdfFileMerger
查看PyPDF2源代码,我们看到merger = PdfFileMerger()
for filename in filenames:
merger.append(filename)
merger.write(output_file_path)
需要传递append()
,然后使用fileobj
函数,将其最后一页作为新文件位置传入。 merge()
使用merge()
执行以下操作(在使用fileobj
打开之前:
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
选项确实接受了一个字符串,并且在这样做时,假设它是一个文件路径并在该位置创建一个文件对象。最终结果与我们试图避免的完全相同。一个append()
对象保持打开文件,直到最终写入文件!
但是,如果我们要么创建文件路径字符串或的文件对象PdfFileReader()
(请参阅编辑2) 路径字符串在传递到PdfFileReader
之前的对象,它将自动为我们创建一个append()
对象的副本,允许Python关闭该文件。
我建议使用更简单的StringIO
,因为其他人报告即使在调用merger.append(file(filename, 'rb'))
后,PdfFileReader
对象也可能在内存中保持打开状态。
希望这有帮助!
编辑:我认为您使用的是writer.close()
,而不是PyPDF2
。如果你不是,我强烈推荐切换,因为PyPDF不再与作者在开发PyPDF2时向Phaseit提供正式祝福。
如果由于某种原因你不能交换到PyPDF2(许可,系统限制等),那么PyPDF
将无法使用PdfFileMerger
。在这种情况下,您可以重用PyPDF2的merge
函数(上面提供的)中的代码来创建文件的副本作为StringIO
对象,并在代码中使用它来代替文件对象。
编辑2:以前使用merger.append(PdfFileReader(file(filename, 'rb')))
的建议根据评论(感谢@Agostino)进行了更改。
答案 1 :(得分:3)
pdfrw包可以一次性读取每个文件,因此不会遇到打开文件太多的问题。 Here是一个示例连接脚本。
相关部分 - 假设inputs
是输入文件名列表,outfn
是输出文件名:
from pdfrw import PdfReader, PdfWriter
writer = PdfWriter()
for inpfn in inputs:
writer.addpages(PdfReader(inpfn).pages)
writer.write(outfn)
免责声明:我是主要的pdfrw作者。
答案 2 :(得分:1)
问题是您只能在任何给定时间打开一定数量的文件。有办法改变这个(http://docs.python.org/3/library/resource.html#resource.getrlimit),但我认为你不需要这个。
您可以尝试关闭for循环中的文件:
input = PdfFileReader()
output = PdfFileWriter()
for file in filenames:
f = open(file, 'rb')
input = PdfFileReader(f)
# Some code
f.close()
答案 3 :(得分:1)
我已经编写了这段代码来帮助回答:-
import sys
import os
import PyPDF2
merger = PyPDF2.PdfFileMerger()
#get PDFs files and path
path = sys.argv[1]
pdfs = sys.argv[2:]
os.chdir(path)
#iterate among the documents
for pdf in pdfs:
try:
#if doc exist then merge
if os.path.exists(pdf):
input = PyPDF2.PdfFileReader(open(pdf,'rb'))
merger.append((input))
else:
print(f"problem with file {pdf}")
except:
print("cant merge !! sorry")
else:
print(f" {pdf} Merged !!! ")
merger.write("Merged_doc.pdf")
在此,我使用了PyPDF2.PdfFileMerger和PyPDF2.PdfFileReader,而不是将文件名明确转换为文件对象
答案 4 :(得分:0)
它可能正是它所说的,你正在开放许多文件。
您可以在循环中明确使用f=file(filename) ... f.close()
,或使用with
语句。这样每个打开的文件都可以正常关闭。