python chunk 方式读取大文件——本质上还是file read自身支持
参考:https://stackoverflow.com/questions/519633/lazy-method-for-reading-big-file-in-python
最优雅方式:
file.readlines() takes in an optional size argument which approximates the number of lines read in the lines returned.
bigfile = open('bigfilename','r')
tmp_lines = bigfile.readlines(BUF_SIZE)
while tmp_lines:
process([line for line in tmp_lines])
tmp_lines = bigfile.readlines(BUF_SIZE)
或者:
To write a lazy function, just use yield
:
def read_in_chunks(file_object, chunk_size=1024):
"""Lazy function (generator) to read a file piece by piece.
Default chunk size: 1k."""
while True:
data = file_object.read(chunk_size)
if not data:
break
yield data
f = open('really_big_file.dat')
for piece in read_in_chunks(f):
process_data(piece)
Read a file in chunks in Python
This article is just to demonstrate how to read a file in chunks rather than all at once.
This is useful for a number of cases, such as chunked uploading or encryption purposes, or perhaps where the file you want to interact with is larger than your machine memory capacity.
- # chunked file reading
- from __future__ import division
- import os
- def get_chunks(file_size):
- chunk_start = 0
- chunk_size = 0x20000 # 131072 bytes, default max ssl buffer size
- while chunk_start + chunk_size < file_size:
- yield(chunk_start, chunk_size)
- chunk_start += chunk_size
- final_chunk_size = file_size - chunk_start
- yield(chunk_start, final_chunk_size)
- def read_file_chunked(file_path):
- with open(file_path) as file_:
- file_size = os.path.getsize(file_path)
- print('File size: {}'.format(file_size))
- progress = 0
- for chunk_start, chunk_size in get_chunks(file_size):
- file_chunk = file_.read(chunk_size)
- # do something with the chunk, encrypt it, write to another file...
- progress += len(file_chunk)
- print('{0} of {1} bytes read ({2}%)'.format(
- progress, file_size, int(progress / file_size * 100))
- )
- if __name__ == '__main__':
- read_file_chunked('some-file.gif')
Also available as a Gist (https://gist.github.com/richardasaurus/21d4b970a202d2fffa9c)
The above will output:
- File size: 698837
- 131072 of 698837 bytes read (18%)
- 262144 of 698837 bytes read (37%)
- 393216 of 698837 bytes read (56%)
- 524288 of 698837 bytes read (75%)
- 655360 of 698837 bytes read (93%)
- 698837 of 698837 bytes read (100%)
Hopefully handy to someone. This of course isn’t the only way, you could also use `file.seek` in the standard library to target chunks.
Processing large files using python
In the last year or so, and with my increased focus on ribo-seq data, I have come to fully appreciate what the term big data means. The ribo-seq studies in their raw forms can easily reach into hundreds of GBs, which means that processing them in both a timely and efficient manner requires some thought. In this blog post, and hopefully those following, I want to detail some of the methods I have come up (read: pieced together from multiple stack exchange posts), that help me take on data of this magnitude. Specifically I will be detailing methods for python and R, though some of the methods are transferrable to other languages.
My first big data tip for python is learning how to break your files into smaller units (or chunks) in a manner that you can make use of multiple processors. Let’s start with the simplest way to read a file in python.
with open("input.txt") as f:
data = f.readlines()
for line in data:
process(line)
This mistake made above, with regards to big data, is that it reads all the data into RAM before attempting to process it line by line. This is likely the simplest way to cause the memory to overflow and an error raised. Let’s fix this by reading the data in line by line, so that only a single line is stored in the RAM at any given time.
with open("input.txt") as f:
for line in f:
process(line)
This is a big improvement, namely it doesn’t crash when fed a big file (though also it’s shorter!). Next we should attempt to speed this up a bit by making use of all these otherwise idle cores.
import multiprocessing as mp
#init objects
pool = mp.Pool(cores)
jobs = []
#create jobs
with open("input.txt") as f:
for line in f:
jobs.append( pool.apply_async(process,(line)) )
#wait for all jobs to finish
for job in jobs:
job.get()
#clean up
pool.close()
Provided the order of which you process the lines don’t matter, the above generates a set (pool) of workers, ideally one for each core, before creating a bunch of tasks (jobs), one for each line, for the workers to do. I tend to use the Pool object provided by the multiprocessing module due to ease of use, however, you can spawn and control individual workers using mp.Process if you want finer control. For mere number crunching, the Pool object is very good.
While the above is now making use of all those cores, it sadly runs into memory problems once again. We specifically use apply_async function so that the pool isn’t blocked while each line processes. However, in doing so, all the data is read into memory once again; this time stored as individual lines associated with each job, waiting inline to be processed. As such, the memory will again overflow. Ideally the method will only read the line into memory when it is its turn to be processed.
import multiprocessing as mp
def process_wrapper(lineID):
with open("input.txt") as f:
for i,line in enumerate(f):
if i != lineID:
continue
else:
process(line)
break
#init objects
pool = mp.Pool(cores)
jobs = []
#create jobs
with open("input.txt") as f:
for ID,line in enumerate(f):
jobs.append( pool.apply_async(process_wrapper,(ID)) )
#wait for all jobs to finish
for job in jobs:
job.get()
#clean up
pool.close()
Above we’ve now changed the function fed to pool of workers to include opening the file, locating the specified line, reading it into memory, and then processing it. The only input now stored for each job spawned is the line number, thereby preventing the memory overflow. Sadly, the overhead involved in having to locate the line by reading iteratively through the file for each job is untenable, getting progressively more time consuming as you get further into the file. To avoid this we can use the seek function of file objects which skips you to a particular location within a file. Combining with the tell function, which returns the current location within a file, gives:
import multiprocessing as mp
def process_wrapper(lineByte):
with open("input.txt") as f:
f.seek(lineByte)
line = f.readline()
process(line)
#init objects
pool = mp.Pool(cores)
jobs = []
#create jobs
with open("input.txt") as f:
nextLineByte = f.tell()
for line in f:
jobs.append( pool.apply_async(process_wrapper,(nextLineByte)) )
nextLineByte = f.tell()
#wait for all jobs to finish
for job in jobs:
job.get()
#clean up
pool.close()
Using seek we can move directly to the correct part of the file, whereupon we read a line into the memory and process it. We have to be careful to correctly handle the first and last lines, but otherwise this does exactly what we set out, namely using all the cores to process a given file while not overflowing the memory.
I’ll finish this post with a slight upgrade to the above as there is a reasonable amount of overhead associated with opening and closing the file for each individual line. If we process multiple lines of the file at a time as a chunk, we can reduce these operations. The biggest technicality when doing this is noting that when you jump to a location in a file, you are likely not located at the start of a line. For a simple file, as in this example, this just means you need to call readline, which reads to next newline character. More complex file types likely require additional code to locate a suitable location to start/end a chunk.
import multiprocessing as mp,os
def process_wrapper(chunkStart, chunkSize):
with open("input.txt") as f:
f.seek(chunkStart)
lines = f.read(chunkSize).splitlines()
for line in lines:
process(line)
def chunkify(fname,size=1024*1024):
fileEnd = os.path.getsize(fname)
with open(fname,'r') as f:
chunkEnd = f.tell()
while True:
chunkStart = chunkEnd
f.seek(size,1)
f.readline()
chunkEnd = f.tell()
yield chunkStart, chunkEnd - chunkStart
if chunkEnd > fileEnd:
break
#init objects
pool = mp.Pool(cores)
jobs = []
#create jobs
for chunkStart,chunkSize in chunkify("input.txt"):
jobs.append( pool.apply_async(process_wrapper,(chunkStart,chunkSize)) )
#wait for all jobs to finish
for job in jobs:
job.get()
#clean up
pool.close()
Anyway, I hope that some of the above was either new or even perhaps helpful to you. If you know of a better way to do things (in python), then I’d be very interested to hear about it. In another post coming in the near future, I will expanded on this code, turning it into a parent class from which create multiple children to use with various file types.
python chunk 方式读取大文件——本质上还是file read自身支持的更多相关文章
- 在python中逐行读取大文件
在我们日常工作中,难免会有处理日志文件的时候,当文件小的时候,基本不用当心什么,直接用file.read()或readlines()就可以了,但是如果是将一个10G大小的日志文件读取,即文件大于内存的 ...
- Python之requests模块-大文件分片上传
最近在做接口测试时,拿到一个分片上传文件的接口,http接口请求头中的Content-Type为multipart/form-data.需要在客户端将大文件分片成数据块后,依次传给服务端,由服务端还原 ...
- python读取大文件只能读取部分的问题
最近准备重新研究一下推荐系统的东西,用到的数据集是Audioscrobbler音乐数据集.我用python处理数据集中artist_data.txt这个文件的时候,先读取每一行然后进行处理: with ...
- Python读取大文件的"坑“与内存占用检测
python读写文件的api都很简单,一不留神就容易踩"坑".笔者记录一次踩坑历程,并且给了一些总结,希望到大家在使用python的过程之中,能够避免一些可能产生隐患的代码. 1. ...
- Python逐块读取大文件行数的代码 - 为程序员服务
Python逐块读取大文件行数的代码 - 为程序员服务 python数文件行数最简单的方法是使用enumerate方法,但是如果文件很大的话,这个方法就有点慢了,我们可以逐块的读取文件的内容,然后按块 ...
- Python读取大文件(GB)
Python读取大文件(GB) - CSDN博客 https://blog.csdn.net/shudaqi2010/article/details/54017766
- 强悍的Python读取大文件的解决方案
这是一道著名的 Python 面试题,考察的问题是,Python 读取大文件和一般规模的文件时的区别,也即哪些接口不适合读取大文件. 1. read() 接口的问题 f =open(filename, ...
- PHP 与Python 读取大文件的区别
php读取大文件的方法 <?php function readFile($file) { # 打开文件 $handle = fopen($file, 'rb'); while (feof($ ...
- formdata方式上传文件,支持大文件分割上传
1.upload.html <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/html"> <h ...
随机推荐
- 第一次android混淆实战
第一次混淆,主要是因为引用本地第三方jar包的问题.虽然说本地第三方jar包自动避免混淆,但一些本地第三方jar包下的一些包要避免混淆.比如: 文中的com.org 这些包名都要避免混淆. 下面是我用 ...
- spring 将配置文件中的值注入 属性
1.编写配置文件 #债权转让 #默认周期 必须大于0 credit.defaultDuration=1 #最小转让金额(元) credit.minBidAmount=1.00 #最小转让时间 到期时间 ...
- 【YOLO】只检测人
一.修改源代码 cfg/coco.data classes= #修改成1 train = /home/pjreddie/data/coco/trainvalno5k.txt valid = coco_ ...
- .net core里用ZXing生成二维码
先获取Nuget包 static void Main(string[] args) { string content = "二维码信息"; BitMatrix byteMatrix ...
- table头部固定,内容滚动
可以设置两个table,th,td得设置宽度: <table> <thead> <tr><th></th&g ...
- java 异常报错总结
1.java.lang.ArithmeticException:这是算数异常 比如分母位0 2. java.lang.ArrayIndexOutOfBoundsException:数组下标越界异常 3 ...
- Android—修改button属性
一般安卓里的普通按钮控件灰灰的,比较单调,我们可以给按钮加上背景图片,或者自定义按钮的圆角,颜色等属性. 下面用代码举例: <Button android:id="@+id/reset ...
- 新版本的molar mass(uva-1586)明明debug过了,各种测试还是WA真是气死我了
#include <bits/stdc++.h> using namespace std; double trans(string a) { stringstream ss; ss< ...
- Django Template(模板系统)
一.Django模板 内置模板标签和过滤器 二.常用操作 两种特殊符号: {{ }} 和 {% %} 变量相关的用: {{ }} 逻辑相关的用: {% %} 2.1 变量 在Django的模 ...
- 第一节:web爬虫之requests
Requests库是用Python编写的,并且Requests是一个优雅而简单的Python HTTP库,在使用Requests库时更加方便,可以节约我们大量的工作,完全满足HTTP测试需求.