Python文本处理之按行处理大文件的方法
以行的形式读出一个文件最简单的方式是使用文件对象的readline()、readlines()和xreadlines()方法。
Python2.2+为这种频繁的操作提供了一个简化的语法——让文件对象自身在行上高效迭代(这种迭代是严格的向前的)。
为了读取整个文件,可能要使用read()方法,且使用字符串的split()来将它拆分WEIGHT行或其他块。
下面是一些例子:
>>>forlineinopen('chap1.txt'):#Python2.2+ ...#processeachlineinsomemanner ...pass ... >>>linelist=open('chap1.txt').readlines() >>>printlinelist[1849], EXERCISE:Workingwithlinesfromalargefile >>>txt=open('chap1.txt').read() >>>fromosimportlinesep >>>linelist2=txt.split(linesep)
如果文件不大,读取整个文件内容也没有关系。但如果是大文件,时间和内存就是要重点关注的了。比如,复杂文档或者活动日志文件,通常有上M,甚至很多G的大小。就算这些文件的内容没有超出可用内存的尺寸,读取他们仍然是相当耗时的。
很明显,如果你需要处理文件的每一行,那就必须读取整个文件;如果可以按序列处理,xreadlines方法是一种更节约内存的方法。但是对于那些仅仅需要一个大文件的一部分行的应用,要获得提高其实并不难。对于这一点,模块“linecache”非常合适。
具有缓存功能的行列表
使用linecache可以直接从一个文件中读取指定行:
>>>importlinecache >>>printlinecache.getline('chap1.txt',1850), PROBLEM:Workingwithlinesfromalargefile
记住,linecache.getline()的计数是从1开始的。
如果有一个即具有“linecache”的效率,又有列表的一些功能的对象就好了。这个对象不仅可以枚举和索引,同时还支持切片。
#------------------cachedlinelist.py--------------------# importlinecache,types classCachedLineList: #Note:inPython2.2+,itisprobablyworthincluding: #__slots__=('_fname') #...andinheritingfrom'object' def__init__(self,fname): self._fname=fname def__getitem__(self,x): iftype(x)istypes.SliceType: return[linecache.getline(self._fname,n+1) forninrange(x.start,x.stop,x.step)] else: returnlinecache.getline(self._fname,x+1) def__getslice__(self,beg,end): #passto__getitem__whichdoesextendedslicesalso returnself[beg:end:1]
使用这个新对象几乎和使用一个由“open(fname).readlines()”创建的列表一样。除了它的效率要更高之外(特别是在内存使用方面):
>>>fromcachedlinelistimportCachedLineList >>>cll=CachedLineList('../chap1.txt') >>>cll[1849] 'PROBLEM:Workingwithlinesfromalargefile\r\n' >>>forlineincll[1849:1851]:printline, ... PROBLEM:Workingwithlinesfromalargefile ---------------------------------------------------------- >>>forlineincll[1853:1857:2]:printline, ... amatterofusingthe'.readline()','.readlines()'and simplifiedsyntaxforthisfrequentoperationbylettingthe
随机行
有时候,特别是为了测试,可能需要检查某些典型的行。人们很容易就误认为一个对文本的前面几行和后面几行有效的处理就能适用任何其他地方。很不幸,很多文件的前几行和最后几行通常都是非典型的:有时候是消息头或注脚,有时候可能是开发时的日志文件的前几行等等。穷举测试整个文件并不是你想要的,通常这样也非常的耗时。
在大多数系统上,查找一个文件中的特定位置要比读出该位置前的所有内容直到到达该位置快的多。
就算使用linecache,要到达缓存行,你也需要一个字节一个字节的读取前面的内容。从一个大文件中找随机行的最快的方式是,先找到一个随机位置,然后读取该位置相对前后的少数字节。
#--------------------randline.py------------------------# #!/usr/bin/python """Iterateoverrandomlinesinafile(reqPython2.2+) Fromcommand-lineuse:%randline.py""" importsys fromosimportstat,linesep fromstatimportST_SIZE fromrandomimportrandrange MAX_LINE_LEN=4096 #--Iterableclass classrandline(object): __slots__=('_fp','_size','_limit') def__init__(self,fname,limit=sys.maxint): self._size=stat(fname)[ST_SIZE] self._fp=open(fname,'rb') self._limit=limit def__iter__(self): returnself defnext(self): ifself._limit<=0: raiseStopIteration self._limit-=1 pos=randrange(self._size) priorlen=min(pos,MAX_LINE_LEN)#maybenearstart self._fp.seek(pos-priorlen) #Addextralinesepatbeg/endincaseposatbeg/end prior=linesep+self._fp.read(priorlen) post=self._fp.read(MAX_LINE_LEN)+linesep begln=prior.rfind(linesep)+len(linesep) endln=post.find(linesep) returnprior[begln:]+post[:endln] #--Useascommand-linetool if__name__=='__main__': fname,numlines=sys.argv[1],int(sys.argv[2]) forlineinrandline(fname,numlines): printline
关于上面的实现,需要注意以下细节:
(1)在行迭代中,相同的行可能会被多次选中。当然,如果你只是从大文件中选很少行的话,这种情况通常不会出现。
(2)既然是选中包含随机位置的行,那就意味着更有可能选择长的行(译注:这是为什么?没有明白)。
本文翻译自TextProcessinginPython
中“PROBLEM:Workingwithlinesfromalargefile”
以上这篇Python文本处理之按行处理大文件的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。