[英]How do I split a huge text file in python
我有一个巨大的文本文件(~1GB),遗憾的是我使用的文本编辑器无法读取这么大的文件。 但是,如果我可以将它分成两到三个部分,我会很好,因此,作为练习,我想用 python 编写一个程序来完成它。
我想我希望程序做的是找到一个文件的大小,将该数字分成几部分,对于每个部分,以块的形式读取到该点,写入文件名.nnn 输出文件,然后读取 -到下一个换行符并写入,然后关闭输出文件等。显然最后一个输出文件只是复制到输入文件的末尾。
你能帮我解决关键的文件系统相关部分:文件大小、分块读取和写入以及读取到换行符吗?
我将首先编写此代码测试,所以没有必要给我一个完整的答案,除非它是单行的 ;-)
linux有一个split命令
split -l 100000 文件.txt
将拆分为 100,000 行大小的文件
检查os.stat()
文件大小和file.readlines([sizehint])
。 这两个函数应该是阅读部分所需的全部内容,希望您知道如何进行写作:)
作为替代方法,使用日志库:
>>> import logging.handlers
>>> log = logging.getLogger()
>>> fh = logging.handlers.RotatingFileHandler("D://filename.txt",
maxBytes=2**20*100, backupCount=100)
# 100 MB each, up to a maximum of 100 files
>>> log.addHandler(fh)
>>> log.setLevel(logging.INFO)
>>> f = open("D://biglog.txt")
>>> while True:
... log.info(f.readline().strip())
您的文件将如下所示:
文件名.txt(文件结尾)
文件名.txt.1
文件名.txt.2
...
filename.txt.10(文件开头)
这是使巨大的日志文件与您的RotatingFileHandler
实现相匹配的快速简便的方法。
现在,有一个 pypi 模块可用于将任何大小的文件拆分为块。 看一下这个
这种生成器方法是一种(缓慢的)方法,可以在不破坏记忆的情况下获得一段线。
import itertools
def slicefile(filename, start, end):
lines = open(filename)
return itertools.islice(lines, start, end)
out = open("/blah.txt", "w")
for line in slicefile("/python27/readme.txt", 10, 15):
out.write(line)
虽然Ryan Ginstrom 的回答是正确的,但它确实需要比它应该更长的时间(正如他已经指出的那样)。 这是一种通过连续迭代打开的文件描述符来绕过对itertools.islice
的多次调用的方法:
def splitfile(infilepath, chunksize):
fname, ext = infilepath.rsplit('.',1)
i = 0
written = False
with open(infilepath) as infile:
while True:
outfilepath = "{}{}.{}".format(fname, i, ext)
with open(outfilepath, 'w') as outfile:
for line in (infile.readline() for _ in range(chunksize)):
outfile.write(line)
written = bool(line)
if not written:
break
i += 1
您可以使用wc
和split
(请参阅相应的联机帮助页)来获得所需的效果。 在bash
:
split -dl$((`wc -l 'filename'|sed 's/ .*$//'` / 3 + 1)) filename filename-chunk.
产生相同 linecount 的 3 个部分(当然最后一个舍入错误),命名为filename-chunk.00
到filename-chunk.02
。
我已经编写了程序,它似乎运行良好。 所以感谢 Kamil Kisiel 让我开始。
(请注意 FileSizeParts() 是此处未显示的函数)
稍后我可能会转而做一个进行二进制读取的版本,看看它是否更快。
def Split(inputFile,numParts,outputName):
fileSize=os.stat(inputFile).st_size
parts=FileSizeParts(fileSize,numParts)
openInputFile = open(inputFile, 'r')
outPart=1
for part in parts:
if openInputFile.tell()<fileSize:
fullOutputName=outputName+os.extsep+str(outPart)
outPart+=1
openOutputFile=open(fullOutputName,'w')
openOutputFile.writelines(openInputFile.readlines(part))
openOutputFile.close()
openInputFile.close()
return outPart-1
用法 - split.py 文件名 splitsizeinkb
import os
import sys
def getfilesize(filename):
with open(filename,"rb") as fr:
fr.seek(0,2) # move to end of the file
size=fr.tell()
print("getfilesize: size: %s" % size)
return fr.tell()
def splitfile(filename, splitsize):
# Open original file in read only mode
if not os.path.isfile(filename):
print("No such file as: \"%s\"" % filename)
return
filesize=getfilesize(filename)
with open(filename,"rb") as fr:
counter=1
orginalfilename = filename.split(".")
readlimit = 5000 #read 5kb at a time
n_splits = filesize//splitsize
print("splitfile: No of splits required: %s" % str(n_splits))
for i in range(n_splits+1):
chunks_count = int(splitsize)//int(readlimit)
data_5kb = fr.read(readlimit) # read
# Create split files
print("chunks_count: %d" % chunks_count)
with open(orginalfilename[0]+"_{id}.".format(id=str(counter))+orginalfilename[1],"ab") as fw:
fw.seek(0)
fw.truncate()# truncate original if present
while data_5kb:
fw.write(data_5kb)
if chunks_count:
chunks_count-=1
data_5kb = fr.read(readlimit)
else: break
counter+=1
if __name__ == "__main__":
if len(sys.argv) < 3: print("Filename or splitsize not provided: Usage: filesplit.py filename splitsizeinkb ")
else:
filesize = int(sys.argv[2]) * 1000 #make into kb
filename = sys.argv[1]
splitfile(filename, filesize)
这对我有用
import os
fil = "inputfile"
outfil = "outputfile"
f = open(fil,'r')
numbits = 1000000000
for i in range(0,os.stat(fil).st_size/numbits+1):
o = open(outfil+str(i),'w')
segment = f.readlines(numbits)
for c in range(0,len(segment)):
o.write(segment[c]+"\n")
o.close()
这是一个 python 脚本,可用于使用subprocess
拆分大文件:
"""
Splits the file into the same directory and
deletes the original file
"""
import subprocess
import sys
import os
SPLIT_FILE_CHUNK_SIZE = '5000'
SPLIT_PREFIX_LENGTH = '2' # subprocess expects a string, i.e. 2 = aa, ab, ac etc..
if __name__ == "__main__":
file_path = sys.argv[1]
# i.e. split -a 2 -l 5000 t/some_file.txt ~/tmp/t/
subprocess.call(["split", "-a", SPLIT_PREFIX_LENGTH, "-l", SPLIT_FILE_CHUNK_SIZE, file_path,
os.path.dirname(file_path) + '/'])
# Remove the original file once done splitting
try:
os.remove(file_path)
except OSError:
pass
您可以在外部调用它:
import os
fs_result = os.system("python file_splitter.py {}".format(local_file_path))
您还可以导入subprocess
并直接在您的程序中运行它。
这种方法的问题是内存使用率高: subprocess
进程创建一个内存占用与您的进程相同大小的分叉,如果您的进程内存已经很重,它会在运行时加倍。 与os.system
相同。
这是执行此操作的另一种纯 python 方式,虽然我没有在大文件上测试过它,但它会更慢但内存更精简:
CHUNK_SIZE = 5000
def yield_csv_rows(reader, chunk_size):
"""
Opens file to ingest, reads each line to return list of rows
Expects the header is already removed
Replacement for ingest_csv
:param reader: dictReader
:param chunk_size: int, chunk size
"""
chunk = []
for i, row in enumerate(reader):
if i % chunk_size == 0 and i > 0:
yield chunk
del chunk[:]
chunk.append(row)
yield chunk
with open(local_file_path, 'rb') as f:
f.readline().strip().replace('"', '')
reader = unicodecsv.DictReader(f, fieldnames=header.split(','), delimiter=',', quotechar='"')
chunks = yield_csv_rows(reader, CHUNK_SIZE)
for chunk in chunks:
if not chunk:
break
# Do something with your chunk here
这是另一个使用readlines()
例子:
"""
Simple example using readlines()
where the 'file' is generated via:
seq 10000 > file
"""
CHUNK_SIZE = 5
def yield_rows(reader, chunk_size):
"""
Yield row chunks
"""
chunk = []
for i, row in enumerate(reader):
if i % chunk_size == 0 and i > 0:
yield chunk
del chunk[:]
chunk.append(row)
yield chunk
def batch_operation(data):
for item in data:
print(item)
with open('file', 'r') as f:
chunks = yield_rows(f.readlines(), CHUNK_SIZE)
for _chunk in chunks:
batch_operation(_chunk)
readlines 示例演示了如何将数据分块以将块传递给需要块的函数。 不幸的是 readlines 在内存中打开整个文件,最好使用阅读器示例来提高性能。 尽管如果您可以轻松地将所需的内容放入内存并需要将其分块处理,这应该就足够了。
您可以实现将任何文件拆分为如下所示的块,这里的 CHUNK_SIZE 为 500000 字节(500kb),内容可以是任何文件:
for idx,val in enumerate(get_chunk(content, CHUNK_SIZE)):
data=val
index=idx
def get_chunk(content,size):
for i in range(0,len(content),size):
yield content[i:i+size]
我需要拆分 csv 文件以导入 Dynamics CRM,因为导入的文件大小限制为 8MB,而我们收到的文件要大得多。 该程序允许用户输入 FileNames 和 LinesPerFile,然后将指定的文件拆分为所需的行数。 我简直不敢相信它的工作速度有多快!
# user input FileNames and LinesPerFile
FileCount = 1
FileNames = []
while True:
FileName = raw_input('File Name ' + str(FileCount) + ' (enter "Done" after last File):')
FileCount = FileCount + 1
if FileName == 'Done':
break
else:
FileNames.append(FileName)
LinesPerFile = raw_input('Lines Per File:')
LinesPerFile = int(LinesPerFile)
for FileName in FileNames:
File = open(FileName)
# get Header row
for Line in File:
Header = Line
break
FileCount = 0
Linecount = 1
for Line in File:
#skip Header in File
if Line == Header:
continue
#create NewFile with Header every [LinesPerFile] Lines
if Linecount % LinesPerFile == 1:
FileCount = FileCount + 1
NewFileName = FileName[:FileName.find('.')] + '-Part' + str(FileCount) + FileName[FileName.find('.'):]
NewFile = open(NewFileName,'w')
NewFile.write(Header)
NewFile.write(Line)
Linecount = Linecount + 1
NewFile.close()
或者,一个 python 版本的 wc 和 split:
lines = 0
for l in open(filename): lines += 1
然后一些代码将第一行/3 读入一个文件,将下一行/3 读入另一个文件,等等。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.