论坛求助与自行摸索相结合,基本解决python大文件解析效率问题。现将过程与结论存档,供有需要者参考。
=================前言====================
看着python很火,就跑到python怀抱蹭蹭,list,dict的普通化应用,让很多编程简便了很多。
感觉还不错,就开始着手写了个常用程序,磕磕碰碰的写完。却发现大型文件处理有严重的效率问题,只好到论坛找高手。
貌似python在csdn的论坛也不火,主要靠管理员wn0112 一直帮忙互相印证改进。折腾来折腾去,效率提升仍旧不是数量级层次。
就在快要放弃,准备用c写模块来实现基础解析功能的时候,看到网上一个用法,灵机一现,总算是解决了。
结果来之不易,多啰嗦几句,感慨感慨。各位看官休怪。
=================结论=======================
环境以下没注明的情况下,python采用python.org的3.6.1版本,机器AMD AthlonII x3 450- *, win10
在讨论过程中发现,几个简单结论,
python的运行速度,受电脑软硬件的影响,差不多有1倍左右的差距。【I7 5500U 2.40G vs AMD Athlon II x3 450-*】【win7 vs win10】【2.7 vs 3.5】由于别人也忙,我手头上也没多少高大上的机器,没有深入。不过告诉我们一个思路,如果有时候效率只是1倍左右的差距,可以考虑升级升级硬件,o(^^)o
python的循环效率还是蛮低的,没事不要用循环。只做个J+=1,循环1200w次,也要1.5秒。多做一个K+=1要3秒。
如果发现有效率问题,能优化算法,优化算法。算法也没法优化,就找模块,找函数。
一、大型文件的读取效率
面对100w行的大型数据
with open(filename,"rb") as f: for fLine in f: pass
rb方式最快,100w行全遍历2.7秒。基本能满足中大型文件处理效率需求。如果rb改为r,慢6倍。
虽然此方式处理文件,fLine为bytes类型,但是python自动断行,仍旧能很好的以行为单位处理读取内容。
如果需要获取行号,则使用:
for lineNum, fline in enumerate(f) :
如果建立索引,以便后续查找定位,不建议使用传统win编程的行号定位。最好记录偏移量来进行定位。
import itertools for lineNum, fline in itertools.dropwhile(lambda x:x[0] < 1073109, enumerate(f)) :
重新定位的效率会高很多很多。
二、文本处理效率
文本处理,一般分为分隔符处理,定长处理。其实就是看重python3.x系列将string固化为unicode字符串,其他使用bytes类型。不是经常处理ascII,unicode不知道这种改进是多好。这也是抛弃2.7直接学3.x的原因。
分隔符有各种split函数,我就不多说了。
这里说定长文件。定长有unicode定长,与ascII定长。
我处理的是ascii定长方式。
以MS系编程经验,全bytes处理,效率最高。所以全用bytes不转换string处理。结果效率堪忧。解析20w行数据,直接上升到12s,已经成为性能瓶颈。
开始使用最简单的循环拆解解析数据,100w行解析20个域,最低只能压倒5秒。用正则压倒3.5秒【如果i7机器,貌似简单循环也能压到3秒】。效率仍旧不满意。
在即将放弃,准备用c重写处理函数自制模块方式提升效率,看c与python类型转换的时候,看到个函数。终于完美解决。代码如下:
import re import time import struct a="张abcdefghijklabcdefghijk" a="000000001111000002222222233333333000000004444444QAZ55555555000000006666666ABC这些事中文字 " ab=a.encode("GBK") aw=(2,3,2,2,2,2,2,2,2,2,2,2) aw=(12,9,4,8,15,3,8,15,3,80,200,15) awf=" ".join(map(lambda x:str(x)+"s",aw)) print(awf) def tt5(argN,argByte): #struck return struct.unpack(argN, argByte) def test5(arg1): ticks=time.clock() for x in range(arg1): trs=tt5(awf, ab) print("test4共计有%d次,耗时%16.6f秒"%(arg1,time.clock()-ticks)) print("结果=",trs) test5(1000000)
struct模块一般用来通信转换为字节流之用,用在此处,100w行解析20个域,可以压在1秒【i7暂时不知道能快多少】基本满足需求。unicode定长方式,我处理这么久很少见到。可以考虑啃啃python3.x的string模块。实在不行用正则,100w行3.5秒,也可以接受。
注意:re模块,如果处理bytes,那么patterns必须是bytes。如果处理string,那么patterns必须是string
csdn的python论坛,人太少。如果python实在有问题,还是找个python人多的论坛吧。
==================过程========================
辛苦了好几天,想了n个方法优化。那是一堆的血泪啊。本想叨叨。
一看,尼玛,过程好多。估计大家也不想看。算了,不说了:)