我有一个包含 20 个文件名的列表,例如 ['file1.txt', 'file2.txt', ...]
。我想编写一个 Python 脚本来将这些文件连接成一个新文件。我可以通过 f = open(...)
打开每个文件,通过调用 f.readline()
逐行读取,然后将每一行写入新文件。对我来说,这似乎不是很“优雅”,尤其是我必须逐行读/写的部分。
在 Python 中是否有更“优雅”的方式来做到这一点?
cat file1.txt file2.txt file3.txt ... > output.txt
之类的操作。在 python 中,如果你不喜欢 readline()
,总是有 readlines()
或只是 read()
。
subprocess
模块运行 cat file1.txt file2.txt file3.txt
命令即可。但我不确定 cat
是否适用于 Windows。
with
语句确保您的文件正确关闭,并遍历文件以获取行,而不是使用 f.readline()
。
这应该这样做
对于大文件:
filenames = ['file1.txt', 'file2.txt', ...]
with open('path/to/output/file', 'w') as outfile:
for fname in filenames:
with open(fname) as infile:
for line in infile:
outfile.write(line)
对于小文件:
filenames = ['file1.txt', 'file2.txt', ...]
with open('path/to/output/file', 'w') as outfile:
for fname in filenames:
with open(fname) as infile:
outfile.write(infile.read())
……还有一个我想到的有趣的:
filenames = ['file1.txt', 'file2.txt', ...]
with open('path/to/output/file', 'w') as outfile:
for line in itertools.chain.from_iterable(itertools.imap(open, filnames)):
outfile.write(line)
遗憾的是,最后一种方法留下了一些打开的文件描述符,GC 无论如何都应该处理这些描述符。我只是觉得这很有趣
它会自动为您逐块读取输入文件,这样效率更高,并且即使某些输入文件太大而无法放入内存,它也可以读取输入文件并且可以正常工作:
import shutil
with open('output_file.txt','wb') as wfd:
for f in ['seg1.txt','seg2.txt','seg3.txt']:
with open(f,'rb') as fd:
shutil.copyfileobj(fd, wfd)
for i in glob.glob(r'c:/Users/Desktop/folder/putty/*.txt'):
好吧,我替换了 for 语句以包含目录中的所有文件,但我的 output_file
很快就开始变得非常大,就像 100 的 gb 一样。
这正是 fileinput 的用途:
import fileinput
with open(outfilename, 'w') as fout, fileinput.input(filenames) as fin:
for line in fin:
fout.write(line)
对于这个用例,它实际上并不比手动迭代文件简单得多,但在其他情况下,使用一个迭代器来迭代所有文件,就好像它们是一个文件一样非常方便。 (此外,fileinput
会在每个文件完成后立即关闭它,这意味着无需with
或 close
每个文件,但这只是节省了一行代码,没什么大不了的。)
fileinput
中还有其他一些不错的功能,例如只需过滤每一行即可对文件进行就地修改。
如评论中所述,以及在另一个 post 中讨论的那样,Python 2.7 的 fileinput
将无法正常工作。此处稍作修改以使代码符合 Python 2.7
with open('outfilename', 'w') as fout:
fin = fileinput.input(filenames)
for line in fin:
fout.write(line)
fin.close()
fileinput
的人都被告知,这是一种将简单的 sys.argv
(或 optparse
/etc 之后剩下的 args )转换为用于琐碎脚本的大型虚拟文件的方法,并且不要想将它用于其他任何事情(即,当列表不是命令行参数时)。或者他们确实学习了,但后来忘记了——我每隔一两年就会重新发现它……
for line in fileinput.input()
不是在这种特殊情况下选择的最佳方式:OP 想要连接文件,而不是逐行读取它们,这在理论上是一个更长的执行过程
我不知道优雅,但这有效:
import glob
import os
for f in glob.glob("file*.txt"):
os.system("cat "+f+" >> OutFile.txt")
cat
可以获取文件列表,因此无需重复调用它。您可以通过调用 subprocess.check_call
而不是 os.system
轻松确保安全
UNIX 命令有什么问题? (假设您不在 Windows 上工作):
ls | xargs cat | tee output.txt
完成这项工作(如果需要,您可以使用子进程从 python 调用它)
cat * | tee output.txt
)。
cat file1.txt file2.txt | tee output.txt
1> /dev/null
来禁用发送到标准输出(在终端中打印)
outfile.write(infile.read()) # time: 2.1085190773010254s
shutil.copyfileobj(fd, wfd, 1024*1024*10) # time: 0.60599684715271s
一个简单的基准测试表明shutil 表现更好。
@inspectorG4dget 答案的替代方法(截至 2016 年 3 月 29 日的最佳答案)。我用 3 个 436MB 的文件进行了测试。
@inspectorG4dget 解决方案:162 秒
以下解决方案:125 秒
from subprocess import Popen
filenames = ['file1.txt', 'file2.txt', 'file3.txt']
fbatch = open('batch.bat','w')
str ="type "
for f in filenames:
str+= f + " "
fbatch.write(str + " > file4results.txt")
fbatch.close()
p = Popen("batch.bat", cwd=r"Drive:\Path\to\folder")
stdout, stderr = p.communicate()
这个想法是利用“旧的好技术”创建一个批处理文件并执行它。它的半蟒蛇,但工作得更快。适用于窗户。
如果目录中有很多文件,那么 glob2
可能是生成文件名列表而不是手动编写它们的更好选择。
import glob2
filenames = glob2.glob('*.txt') # list of all .txt files in the directory
with open('outfile.txt', 'w') as f:
for file in filenames:
with open(file) as infile:
f.write(infile.read()+'\n')
glob2
而不是 glob
模块或 pathlib
中的通配功能?
查看 File 对象的 .read() 方法:
http://docs.python.org/2/tutorial/inputoutput.html#methods-of-file-objects
您可以执行以下操作:
concat = ""
for file in files:
concat += open(file).read()
或更“优雅”的python方式:
concat = ''.join([open(f).read() for f in files])
根据这篇文章:http://www.skymind.com/~ocrow/python_string/ 也是最快的。
如果文件不是很大:
with open('newfile.txt','wb') as newf:
for filename in list_of_files:
with open(filename,'rb') as hf:
newf.write(hf.read())
# newf.write('\n\n\n') if you want to introduce
# some blank lines between the contents of the copied files
如果文件太大而无法完全读取并保存在 RAM 中,则算法必须稍有不同,以通过固定长度的块读取要在循环中复制的每个文件,例如使用 read(10000)
。
os.open
和 os.read
,因为普通的 open
使用 Python 对 C 的 stdio 的包装器,这意味着 1 或 2 个额外的缓冲区进入您的方法。
def concatFiles():
path = 'input/'
files = os.listdir(path)
for idx, infile in enumerate(files):
print ("File #" + str(idx) + " " + infile)
concat = ''.join([open(path + f).read() for f in files])
with open("output_concatFile.txt", "w") as fo:
fo.write(path + concat)
if __name__ == "__main__":
concatFiles()
import os
files=os.listdir()
print(files)
print('#',tuple(files))
name=input('Enter the inclusive file name: ')
exten=input('Enter the type(extension): ')
filename=name+'.'+exten
output_file=open(filename,'w+')
for i in files:
print(i)
j=files.index(i)
f_j=open(i,'r')
print(f_j.read())
for x in f_j:
outfile.write(x)
不定期副业成功案例分享
shutil.copyfileobj
答案会快得多。