31

我有一个巨大的文本文件(~1GB),遗憾的是我使用的文本编辑器不会读取这么大的文件。但是,如果我可以将它分成两三个部分,我会很好,所以,作为一个练习,我想用 python 编写一个程序来完成它。

我想我想让程序做的是找到一个文件的大小,把这个数字分成几部分,然后对于每个部分,以块的形式读取到那个点,写入文件名.nnn 输出文件,然后向上读取-到下一个换行符并写入,然后关闭输出文件等。显然最后一个输出文件只是复制到输入文件的末尾。

您能帮我解决与文件系统相关的关键部分:文件大小、分块读取和写入以及读取换行符吗?

我将首先编写此代码测试,因此无需给我一个完整的答案,除非它是单行的;-)

4

15 回答 15

43

linux有一个split命令

拆分 -l 100000 文件.txt

将拆分为相等的 100,000 行大小的文件

于 2010-02-04T22:42:29.323 回答
16

检查os.stat()文件大小和file.readlines([sizehint]). 这两个功能应该是您阅读部分所需的全部,希望您知道如何进行写作:)

于 2008-11-14T23:18:32.777 回答
9

作为替代方法,使用日志库:

>>> 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())

您的文件将如下所示:

filename.txt(文件结尾)
filename.txt.1
filename.txt.2
...
filename.txt.10(文件开头)

这是使大型日志文件与您的RotatingFileHandler实现相匹配的一种快速简便的方法。

于 2012-05-15T11:04:13.907 回答
9

现在,有一个可用的 pypi 模块,您可以使用它来将任何大小的文件拆分成块。看一下这个

https://pypi.org/project/filesplit/

于 2018-06-07T18:38:57.410 回答
6

不要忘记用于随机访问文件的seek()mmap() 。

def getSomeChunk(filename, start, len):
    fobj = open(filename, 'r+b')
    m = mmap.mmap(fobj.fileno(), 0)
    return m[start:start+len]
于 2010-02-04T22:53:33.493 回答
6

这种生成器方法是一种(慢速)方法,可以在不占用内存的情况下获得一段线。

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)
于 2010-02-04T23:42:15.427 回答
5

虽然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
于 2014-12-24T19:53:47.910 回答
2

您可以使用wcand split(请参阅相应的联机帮助页)来获得所需的效果。在bash

split -dl$((`wc -l 'filename'|sed 's/ .*$//'` / 3 + 1)) filename filename-chunk.

产生相同行数的 3 个部分(当然最后一个有舍入误差),命名filename-chunk.00filename-chunk.02.

于 2008-11-15T00:11:38.537 回答
2

我已经编写了程序,它似乎工作正常。感谢 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
于 2008-11-16T20:54:21.537 回答
2

用法 - 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)
于 2015-10-15T14:39:47.367 回答
2

这是一个可用于拆分大文件的 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 在内存中打开整个文件,最好使用阅读器示例来提高性能。尽管如果您可以轻松地将所需的内容放入内存并需要分块处理它就足够了。

于 2017-09-26T17:30:31.647 回答
1

这对我有用

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()
于 2013-12-02T19:05:08.807 回答
1

您可以实现将任何文件拆分为如下所示的块,这里 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]
于 2020-10-02T09:38:24.947 回答
0

我需要拆分 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()
于 2014-05-24T17:16:39.660 回答
-2

或者,python 版本的 wc 和 split:

lines = 0
for l in open(filename): lines += 1

然后一些代码将第一行 / 3 读入一个文件,将下一行 / 3 读入另一个文件,等等。

于 2008-11-15T18:05:32.627 回答