如何避免使用readlines()?
我需要处理超级大的文本输入文件,通常我会用 .readlines() 方法先把整个文件读进来,然后把它变成一个列表。
我知道这样做会占用很多内存,而且速度也比较慢,但我还需要利用列表的特性来处理特定的行,比如下面这样:
#!/usr/bin/python
import os,sys
import glob
import commands
import gzip
path= '/home/xxx/scratch/'
fastqfiles1=glob.glob(path+'*_1.recal.fastq.gz')
for fastqfile1 in fastqfiles1:
filename = os.path.basename(fastqfile1)
job_id = filename.split('_')[0]
fastqfile2 = os.path.join(path+job_id+'_2.recal.fastq.gz')
newfastq1 = os.path.join(path+job_id+'_1.fastq.gz')
newfastq2 = os.path.join(path+job_id+'_2.fastq.gz')
l1= gzip.open(fastqfile1,'r').readlines()
l2= gzip.open(fastqfile2,'r').readlines()
f1=[]
f2=[]
for i in range(0,len(l1)):
if i % 4 == 3:
b1=[ord(x) for x in l1[i]]
ave1=sum(b1)/float(len(l1[i]))
b2=[ord(x) for x in str(l2[i])]
ave2=sum(b2)/float(len(l2[i]))
if (ave1 >= 20 and ave2>= 20):
f1.append(l1[i-3])
f1.append(l1[i-2])
f1.append(l1[i-1])
f1.append(l1[i])
f2.append(l2[i-3])
f2.append(l2[i-2])
f2.append(l2[i-1])
f2.append(l2[i])
output1=gzip.open(newfastq1,'w')
output1.writelines(f1)
output1.close()
output2=gzip.open(newfastq2,'w')
output2.writelines(f2)
output2.close()
总的来说,我想要读取整个文本的每第四行,但如果这第四行符合我想要的条件,我就会把这四行合并到一起。那么,有没有办法不使用 readlines() 来实现这个呢?谢谢!
编辑:
嗨,实际上我自己找到了一个更好的方法:
import commands
l1=commands.getoutput('zcat ' + fastqfile1).splitlines(True)
l2=commands.getoutput('zcat ' + fastqfile2).splitlines(True)
我觉得 'zcat' 超级快……
用 readlines() 大约花了15分钟,而用 zcat 只花了1分钟……
6 个回答
1
下面是如何打印出所有包含 foo
的行以及它之前的三行:
f = open(...)
prevlines = []
for line in f:
prevlines.append(line)
del prevlines[:-4]
if 'foo' in line:
print prevlines
如果你同时在读取两个文件(并且这两个文件的行数相等),可以这样做:
f1 = open(...)
f2 = open(...)
prevlines1 = []
for line1 in f1:
prevlines1.append(line1)
del prevlines1[:-4]
line2 = f2.readline()
prevlines2.append(line2)
del prevlines2[:-4]
if 'foo' in line1 and 'bar' in line2:
print prevlines1, prevlines2
2
一种简单的方法是,
(伪代码,可能有错误,仅用于说明)
a=gzip.open()
b=gzip.open()
last_four_a_lines=[]
last_four_b_lines=[]
idx=0
new_a=[]
new_b=[]
while True:
la=a.readline()
lb=b.readline()
if (not la) or (not lb):
break
if idx % 4==3:
a_calc=sum([ something ])/len(la)
b_calc=sum([ something ])/len(lb)
if a_calc and b_calc:
for line in last_four_a_lines:
new_a.append(line)
for line in last_four_b_lines:
new_b.append(line)
last_four_a_lines.append(la)
del(last_four_a_lines[0])
last_four_b_lines.append(lb)
del(last_four_b_lines[0])
idx+=1
a.close()
b.close()
6
如果你能把代码改写成逐行读取文件,那你可以简单地用 for line in file
这样的方式来遍历文件的每一行,而不需要一次性把整个文件都读到内存里。不过,由于你的文件访问方式看起来更复杂,你可以使用一个叫做 生成器 的东西来替代 readlines()
。一种实现方法是使用 itertools.izip
或者 itertools.izip_longest
:
def four_at_a_time(iterable):
"""Returns an iterator that returns a 4-tuple of objects at a time from the
given iterable"""
args = [iter(iterable) * 4]
return itertools.izip(*args)
...
l1 = four_at_a_time(gzip.open(fastqfile1, 'r'))
l2 = four_at_a_time(gzip.open(fastqfile2, 'r'))
for i, x in enumerate(itertools.izip(l1, l2))
# x is now a 2-tuple of 4-tuples of lines (one 4-tuple of lines from the first file,
# and one 4-tuple of lines from the second file). Process accordingly.