手记

Python大文件解析的效率问题

论坛求助与自行摸索相结合,基本解决python大文件解析效率问题。现将过程与结论存档,供有需要者参考。

=================前言====================

看着python很火,就跑到python怀抱蹭蹭,list,dict的普通化应用,让很多编程简便了很多。

感觉还不错,就开始着手写了个常用程序,磕磕碰碰的写完。却发现大型文件处理有严重的效率问题,只好到论坛找高手。

貌似python在csdn的论坛也不火,主要靠管理员wn0112 一直帮忙互相印证改进。折腾来折腾去,效率提升仍旧不是数量级层次。

就在快要放弃,准备用c写模块来实现基础解析功能的时候,看到网上一个用法,灵机一现,总算是解决了。

结果来之不易,多啰嗦几句,感慨感慨。各位看官休怪。

=================结论=======================

环境以下没注明的情况下,python采用python.org的3.6.1版本,机器AMD AthlonII x3 450- *, win10

在讨论过程中发现,几个简单结论,


  1. python的运行速度,受电脑软硬件的影响,差不多有1倍左右的差距。【I7 5500U 2.40G vs AMD Athlon II x3 450-*】【win7 vs win10】【2.7 vs 3.5】由于别人也忙,我手头上也没多少高大上的机器,没有深入。不过告诉我们一个思路,如果有时候效率只是1倍左右的差距,可以考虑升级升级硬件,o(^^)o

  2. python的循环效率还是蛮低的,没事不要用循环。只做个J+=1,循环1200w次,也要1.5秒。多做一个K+=1要3秒。

  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个方法优化。那是一堆的血泪啊。本想叨叨。

一看,尼玛,过程好多。估计大家也不想看。算了,不说了:)

原文出处

0人推荐
随时随地看视频
慕课网APP