Ответ 1
Отметьте os.stat()
размер файла и file.readlines([sizehint])
. Эти две функции должны быть все, что вам нужно для чтения, и, надеюсь, вы знаете, как это сделать:)
У меня огромный текстовый файл (~ 1 ГБ), и, к сожалению, текстовый редактор, который я использую, не будет читать такой большой файл. Однако, если я могу просто разбить его на две или три части, я буду в порядке, поэтому, как упражнение, я хотел написать программу на python, чтобы сделать это.
То, что я думаю, что я хочу, чтобы программа выполняла, - это найти размер файла, разделить это число на части, а для каждой части - читать до этой точки в кусках, записывая в выходной файл filename.nnn, затем прочитайте до следующего разрыва строки и напишите это, затем закройте выходной файл и т.д. Очевидно, что последний выходной файл просто копирует в конец входного файла.
Можете ли вы мне помочь с ключевыми файлами, связанными с файлами: файлы, чтение и запись в кусках и чтение на разрыв строки?
Сначала я буду писать этот тест кода, поэтому нет необходимости давать мне полный ответ, если только его однострочный; -)
Отметьте os.stat()
размер файла и file.readlines([sizehint])
. Эти две функции должны быть все, что вам нужно для чтения, и, надеюсь, вы знаете, как это сделать:)
Linux имеет команду split
split -l 100000 file.txt
разделился бы на файлы с размером в 100 000 строк
Как альтернативный метод, используя библиотеку протоколирования:
>>> 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
.
не забывайте искать() и mmap() для произвольного доступа к файлам.
def getSomeChunk(filename, start, len):
fobj = open(filename, 'r+b')
m = mmap.mmap(fobj.fileno(), 0)
return m[start:start+len]
Этот метод генератора - это (медленный) способ получить фрагмент строк без раздувания вашей памяти.
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)
Вы можете использовать wc
и split
(см. соответствующие manpages), чтобы получить желаемый эффект. В bash
:
split -dl$((`wc -l 'filename'|sed 's/ .*$//'` / 3 + 1)) filename filename-chunk.
производит 3 части одного и того же линейного набора (с ошибкой округления в последнем, конечно), с именем filename-chunk.00
до filename-chunk.02
.
Я написал программу, и она работает нормально. Так спасибо Камилю Кисиэлю за то, что вы меня начали.
(Обратите внимание, что 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
В то время как ответ Райана Гинстрома верен, на это требуется больше времени (как он уже отмечал). Здесь можно обойти множественные вызовы 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
использование - 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()
Или, версия wc и split python:
lines = 0
for l in open(filename): lines += 1
Затем некоторый код для чтения первых строк /3 в один файл, следующих строк /3 в другой и т.д.
У меня было требование разделить файлы csv для импорта в Dynamics CRM, так как ограничение на размер файла для импорта составляет 8 МБ, а файлы, которые мы получаем, намного больше. Эта программа позволяет пользователю вводить имена файлов и 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()
Вот питон script, который вы можете использовать для разделения больших файлов с помощью 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 = files.yield_csv_rows(reader, CHUNK_SIZE)
for chunk in chunks:
if not chunk:
break
# Do something with your chunk here