Python에서 거대한 텍스트 파일을 어떻게 분할합니까?
-
08-07-2019 - |
문제
거대한 텍스트 파일 (~ 1GB)이 있으며 슬프게도 내가 사용하는 텍스트 편집기는 큰 파일을 읽지 않습니다. 그러나, 단지 두 부분으로 나눌 수 있다면 괜찮을 것입니다. 운동으로 Python으로 프로그램을 작성하고 싶었습니다.
프로그램이 원하는 것은 파일의 크기를 찾고 해당 숫자를 부품으로 나누고 각 부분에 대해 chunks의 해당 지점까지 읽는 것입니다. 파일 이름.nnn 출력 파일을 한 다음 다음 줄 브레이크를 읽고 쓰기를 읽은 다음 출력 파일 등을 닫습니다. 분명히 마지막 출력 파일은 입력 파일의 끝에 사본 만 있습니다.
주요 파일 시스템 관련 부분과 같은 파일 크기, 읽기 및 쓰기 및 라인 브레이크를 읽을 수 있습니까?
나는이 코드 테스트 우선을 쓸 것이므로, 하나의 라이너가 아니라면 나에게 완전한 답변을 줄 필요가 없다 ;-)
해결책
체크 아웃 os.stat()
파일 크기 및 file.readlines([sizehint])
. 이 두 기능은 읽기 부분에 필요한 모든 것이되어야하며, 글쓰기를 수행하는 방법을 알고 있기를 바랍니다. :)
다른 팁
리눅스에는 분할 명령이 있습니다
분할 -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
구현.
이 생성기 방법은 메모리를 날려 버리지 않고 선을 얻는 (느린) 방법입니다.
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
원하는 효과를 얻으려면 (각각의 맨 페지 참조). ~ 안에 bash
:
split -dl$((`wc -l 'filename'|sed 's/ .*$//'` / 3 + 1)) filename filename-chunk.
동일한 선형의 3 부분을 생성합니다 (물론 마지막으로 반올림 오류가 있음) filename-chunk.00
에게 filename-chunk.02
.
잊지 마세요 찾다. 목표물 탐색() 그리고 mmap () 파일에 대한 무작위 액세스.
def getSomeChunk(filename, start, len):
fobj = open(filename, 'r+b')
m = mmap.mmap(fobj.fileno(), 0)
return m[start:start+len]
하는 동안 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
이제 모든 크기의 파일을 청크로 분할하는 데 사용할 수있는 PYPI 모듈이 있습니다. 이것 좀 봐
나는 프로그램을 작성했는데 잘 작동하는 것 같습니다. 그래서 나를 시작하게 해주신 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 filename 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 및 분할의 파이썬 버전 :
lines = 0
for l in open(filename): lines += 1
그런 다음 첫 번째 줄/3을 하나의 파일로, 다음 줄/3을 다른 줄에 읽는 일부 코드.
가져 오기의 파일 크기 제한이 8MB이고 수신 된 파일이 훨씬 더 크기 때문에 가져 오기 CSV 파일을 Dynamics CRM으로 분할해야합니다. 이 프로그램을 사용하면 사용자가 파일 이름과 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()
다음은 큰 파일을 사용하여 사용할 수있는 파이썬 스크립트입니다. 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
.
다음은이 작업을 수행하는 또 다른 순수한 파이썬 방법이 있습니다.하지만 거대한 파일에서 테스트하지는 않았지만 느리게 진행되지만 메모리에 기대어 있습니다.
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)