项目最近遇到一个需求:
metafield_1|metafield_2|...|metafield_n|keyfield_1|...|keyfield_4|valuefield_1|valuefield_2|....|valuefield_m
除开业务细节,这个任务本质是:
Python很适合完成这种文本处理任务,字符串重复判断这种任务可以使用dict来完成,本文中不做深入探讨。本文想探讨的是在给定了key字段在字段列表中开始下标和key字段个数后,如何在整行字符串中定位到key字符串的起始位置。简而言之,就是确定keyfield_1前一个和keyfield_p后一个“|”字符的位置。
解决这个问题,我想到了三种思路:
有同学会说方法1既然每个字段都已经分割开了,将其按照顺序组合就能得到keyfields_string,为何还要查找“|”字符的位置,我想说在这里只是比较在字符串中查找子串
的各种方法。
针对以上三个思路,我一共有七种实现,后面会对比其效率:
def get_pos_split(line, key_start):
pos = 0
tmp_line_list = line.split('|')
for i in xrange(key_start):
if i >= len(tmp_line_list):
return len(line)
pos += len(tmp_line_list[i]) + 1
return pos
这个思路我写了三种方法,分别用 index/find来实现,需要注意的是,index函数在未找到子串的情况下会抛出ValueError错误,需要用try except处理,而find在找不到子串的情况下返回-1,两者效率基本一致。并且在查找下一个子串的方式上有少许不同,一种是当找到当前子串位置后,记录下该位置,然后下一次从本次找到的位置+1开始查找,另一种是每找到一个子串,就去掉前缀部分,然后下一次在剩下的字符串中查找。
#使用find查找,记录查找位置,下一次从本次找到的位置+1开始查找
def get_pos_find(line, key_start):
if key_start == 0:
return 0
pos = line.find('|')
while pos >= 0 and key_start > 1:
pos = line.find('|', pos+1)
key_start -= 1
return len(line) if pos == -1 else pos+1
#使用index查找,记录查找位置,下一次从本次找到的位置+1开始查找
def get_pos_index(line, key_start):
pos = 0
for i in xrange(key_start):
try:
pos = line.index('|', pos+1)
except ValueError,e:
return len(line)
return 0 if pos == 0 else pos+1
#使用index查找,每次找到第一个子串后,就去掉前缀部分,拷贝后缀部分,后续不断在后缀部分查找
def get_pos_index_2(line, key_start):
tmp_line = line
pos = 0
for i in xrange(key_start):
try:
pos += tmp_line.index('|')+1
tmp_line = tmp_line[tmp_line.index('|')+1:]
except ValueError, e:
return len(line)
return pos
针对这个思路,分别使用正则表达式模块,列表推导式以及lambda、map、filter组合方式实现。
#通过正则表达式re模块查找匹配所有子串位置
def get_pos_re(line, key_start):
pos_idx = [p.start() for p in re.finditer('\|', line)]
return 0 if key_start == 0 else (pos_idx[key_start-1]+1 if key_start <= len(pos_idx) else len(line))
#通过列表推导式(list comprehensions)实现
def get_pos_lc(line, key_start):
pos_idx = [i for i, x in enumerate(line) if x == '|']
return 0 if key_start == 0 else (pos_idx[key_start-1]+1 if key_start <= len(pos_idx) else len(line))
#通过 lambda、map、filter 组合实现
def get_pos_filter(line, key_start):
def func_in(t):
return t[0] if t[1] == '|' else -1
pos_idx = filter(lambda x: x!=-1, map(func_in, enumerate(line)))
return 0 if key_start == 0 else (pos_idx[key_start-1]+1 if key_start <= len(pos_idx) else len(line))
首先,测试在相同单条记录,不同的记录条数条件下,各种方法的耗时,结果如上图所示。
然后,测试在记录条数一定,不同记录长度条件下,各种方法耗时,结果如上图所示。
第三,测试在相同单条记录,相同记录条数情况下取不同位置的字段各种方法耗时,结果如上图所示。
通过测试对比可以看到,字符串分割
和逐个查找子串位置
的思路在总体上都比定位所有子串
位置的思路效率更高。
逐个查找子串位置
思路中通过find和index定位子串位置的效率最高,拆分子串的方式次之。影响性能的因素是单条记录长度以及所需要查找的字段位置。
字符串分割
,影响性能的因素是单条记录长度以及所需要查找的字段位置。
定位所有子串
因为要定位到每个字段的位置,相当于扫描全数据,所以效率最低。在这个思路的三种方法中,正则表达式的实现效率最高,其他两种效率都很差。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有