如何加速以下python3代码?

时间:2017-06-08 20:27:21

标签: python-3.x

所以我试图解析一个巨大的文件,下面的代码需要很长时间才能解析。文件大小为2GB。我希望有些人可以帮助我加快速度。

import os, shlex

def extractLinkField(inDir, outDir):
    fileList = os.listdir(inDir)
    links = set()

    for File in fileList:
        print(File)
        with open(os.path.join(inDir, File), encoding="utf8") as inFile:
            for line in inFile:
                try:
                    links.add(shlex.split(line)[2])
                except Exception:
                    continue

        outFile = open(os.path.join(outDir, 'extractedLinks.txt'), 'a+', encoding="utf8")
        for link in list(links):
            outFile.write(link + '\n')
        outFile.close()

Path = os.path.join(os.getcwd(), 'logs')
extractLinkField(Path, os.getcwd())

文件格式如下:

90 "m  z pd gk y xr   vo" "n l v   ogtc  dj wzb" "d  zi  pfgyo b tmhek" "df qu  venr ls hzw j"
82 "p  wgd lv f kt eb uq" " ij   cw  v a r y qp" "  pf qdlcgm jz  os y" "f xm   n  cr  ublzig"
89 "c  pgib  a   ost whk" "ria m h  fvcb  es  z" "qzoy g xbr      makc" "ms    lqc v  ektb w "
66 "zxm pe hb  vi   dj  " "rg  ebfwp y  zv oakm" "b nut ko je  m  crsh" " imsxtzfw  g ka j l "
2 "uyhnpt  l dj qak    " "o hned j  pqub t a  " "v  hlyc   afwi sgr p" "h wtvi g o  nc sujqx"
17 "apo ufliz  qctbd xh " "k  lxgbrcwzf mnhtq p" "z    gk   m   rsbu l" "  ds  m au w cior   "
9 "  h t  ac  jpn ok mz" "aty rs w box vk zefp" "nm fbc x egt  zruap " "xg  oi j z wyf v dqp"
82 "xs q  ve     k oi c " " z lfa  dwiprxb ku g" "kua p  f  b oqz jrt " "   t wlvy d po qrx e"
51 "cx   iq wuvhb gkmo y" " u p yx    bv mjz  r" "oatc wuxd yfgjs  ri " "vbg  w     h ife myl"
91 "cdqkp rn  u ow   h f" "ko rt y c eis d q jl" "  lv fe r zpju yw   " "  wz  vtxa  jn lg  s"
83 "bts   dl kjycre ozv " " k  i q m r ypsu lh " "pr exw sznqa  yvu i " "  uq   tzk nomrx  e "

请注意,引号包含的文件中的字符串不应拆分,必须整体解析(仍包含在引号中)

Sample output and directory structure:

1 个答案:

答案 0 :(得分:2)

显然,罪魁祸首是shlex.split()。这是一个相当昂贵的操作(为每个拆分创建一个包含大量样板的全新对象),因此如果您的数据遵循所呈现的格式,您可以尝试手动解析数据。

所以,这是一种方法,对{1}​​}的样本数据采用相同的方法:

shlex.split()

def manual_split(data): data = data.strip() # clear artifacts tokens = [] head = 0 open_quote = False while True: if open_quote: quote = data.find('"', head) if quote == -1: # this should not happen, no matching quotes break tokens.append(data[head:quote]) head = quote + 1 open_quote = False else: space = data.find(' ', head) quote = data.find('"', head) if space == -1 and quote == -1: # nothing more to split break if space < quote: if not tokens or space - head > 1: tokens.append(data[head:space]) head = space + 1 open_quote = False else: open_quote = True head = quote + 1 if head < len(data): # add leftovers, if any, as the last token tokens.append(data[head:]) return tokens 相比,在这里运行相同的样本数据(包括循环)有时候适合您:

shlex.split()

因此,速度提高了12倍以上。但是我们可以做得更好......这种方法的问题在于它有很多慢shlex.split: 10,000 loops: 11.51 s, per loop: 1.151 ms manual_split: 10,000 loops: 0.951 s, per loop: 95.11 µs 次调用(尽管逐个字符会更慢)和Python端的字符串改组,因此快速的C端不会没有机会发挥它的魔力。如果你要在C中实现它(通过一些优化)并将其作为一个模块加载它会非常快,但是唉......

所以,我认为str.find()可能会更快地执行它,因为它主要在事物的C端执行,即使你给它更复杂的规则,它应该能够胜过纯Python字符串搜索&amp;操纵足够大的数据。那么,下一个候选人:

regex

现在我们有了最终的基准:

import re

FIELDS_PATTERN = re.compile(r"(?:\"(.*?)\"|(\S+))")

def regex_split(data):
    return [x[0] or x[1] for x in FIELDS_PATTERN.findall(data)]

是的,正则表达式几乎比shlex.split: 10,000 loops: 11.51 s, per loop: 1.151 ms manual_split: 10,000 loops: 0.951 s, per loop: 95.11 µs regex_split: 10,000 loops: 0.482 s, per loop: 48.16 µs 快24倍!它们都会为您的测试数据生成相同的拆分结果。

在你跳入并开始shlex.split()之前

但是,你需要彻底测试这两者以确保它们适合你的数据 - 例如,他们不会识别转义报价或特殊的POSIX解开引用字符串,因此如果您的数据中存在此类情况,则必须将其考虑在内。

另外,无关,如果你想加一点点加速,立即写入你的输出,而不是存储在一个集合中,以便以后循环它:

shlex

假设您将在您的巨大输入文件中收集数百万行,这可能会减少一两秒......