2017-06-08 138 views
-3

所以我試圖解析一個巨大的文件,下面的代碼花費太長的時間來解析。該文件的大小是2GB。我希望有人能幫助我加快速度。如何加快下面的python3代碼?

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:

+0

如果你要投我的問題,請解釋你爲什麼這樣做。謝謝。 – KingMak

+2

你能否請求包括說你要解析的文件的10行,以便我可以嘗試給你寫一個快速的Pandas解析器? – Matt

+0

是的,我現在要做@Matt – KingMak

回答

2

罪魁禍首是,很明顯,shlex.split()。這是一個相當昂貴的操作(創建一個全新的對象,每個分割都有大量樣板),所以如果數據遵循所提供的格式,則可以嘗試手動解析數據。

所以,這裏是作爲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(),運行在相同的樣本數據(包括環路)這裏有一些時間爲您提供:

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 

因此,速度要快12倍以上。但是我們可以做得更好......這種方法的問題在於它有很多緩慢的調用(儘管逐字符會更慢)並且在Python端進行了字符串混洗,因此快速C端不會獲得機會去做它的魔法。如果你要在C中實現這個(有一些優化),並將其作爲模塊加載它會非常快速,但可惜...

所以,我認爲regex可能會更快,因爲它主要執行事情的C面,即使你給它更復雜的規則,它應該能夠超越純Python字符串搜索足夠大的數據的操縱。那麼,接下來的候選人:

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()更快!它們都爲您的測試數據產生相同的分割結果。

你在跳,踢了shlex之前,需要進行全面測試這兩個以確保它們適合你的數據 - 例如,他們不承認轉義引號或引用字符串的特殊POSIX解體使如果你的數據中有這種情況,你必須考慮到這一點。

而且,不相關的,如果你想添加的加快一點點,寫信給你的輸出就會立刻而不是在一組只是遍歷它在以後的時間存儲:

with open(os.path.join(out_dir, 'extractedLinks.txt'), 'a+', encoding="utf8") as out_file: 
    links = set() # temp store to ensure uniqueness... 
    for current_file in file_list: 
     with open(os.path.join(in_dir, current_file), encoding="utf8") as in_file: 
      for line in in_file: 
       try: 
        link = shlex.split(line)[2] # or whatever other function 
        if link not in links: 
         links.add(link) 
         out_file.write(link + "\n") 
       except Exception: 
        continue 

下假設你將在你那個巨大的輸入文件中收集數百萬行,這可能會削減一兩秒......

+0

不錯的答案,我打算在熊貓版本中下降,但它似乎是你的基準否定了需要 – Matt

+0

哇!非常感謝你的細節。我從你的回答中學到了很多東西。 – KingMak