努力做個Pragmatic Programmer

        在公司裏,由於整個開發流程相對規範,整天都是拿着文檔做開發,動腦筋也不太多了,久而久之難免會感到厭煩,還好有一些不需要循規蹈矩的小task可以調和一下。事實上,對於這些小task,Leader通常都會很頭痛,因爲都是一些如果手工完成都會讓人抓狂的,繁瑣而無聊的工作。每每接到這些看似很無聊的task,我都會考慮用較爲聰明的方式將其完成。雖然俺的勞動力低廉,但是不想被這些無聊的工作壞了心情,也正好可以動動自己那快要生鏽的腦筋。
        這次來的task有點棘手:在3萬多行的文本中找出符合要求的文本段。這些文本段當中,可能有一部分是相同的,所以還要統計相同文本段出現的次數,最後以一個統一的格式輸出到一個文件中。既然是文本的處理,最自然就是想到了處理文本的利器:Python。好,明確了task,也選好了工具,開工!
        首先我大致看了一下整個文本的組成,發現文本無非是由兩種類似的文本段組成的:包含字符串"Servelet.Engine.Transports"的文本以及包含"Thread"的文本。而task的要求則是從中挖出包含"Servelet.Engine.Transports"的文本段,並且在此基礎上統計相同文本段出現的次數。既然如此,就不要一步到位了,來個步步爲營吧——先將包含"Thread"的文本段給過濾掉,得到只包含"Servelet.Engine.Transports"的文本,最後再做統計。那麼,問題就細化爲如何過濾文本了。於是,我翻閱了一下文檔之後,寫出了以下的代碼:

None.gifimport re
None.giflog 
= open("20060323.txt", "r"
)
None.gifoutput 
= open("servlet.txt", "w"
)
None.gifnum 
=
0
None.giffor line in
log.readlines():
None.gif    
if re.search("Servlet.Engine.Transports"
, line):
None.gif        num 
= 1

None.gif        output.write(line)
None.gif    
elif line == "\n" and num == 1 :
None.gif        output.write(line)
None.gif        num 
=
0
None.gif         
None.giflog.close()
None.gifoutput.close()


真的很簡短喔!嘿嘿,注意啦!我可不是在自賣自誇哦,偶是在誇着Python提供了很不錯的API,讓我可以用很簡單的方式完成了所需的工作。對於以上代碼,需要注意的地方是re這個module以及module中search函數,還有關於文件的簡單操作。由於open函數存在於__builtins__中,因此不需要import任何的module就可以直接使用了,而open函數返回的正是一個file類型的對象。對於open函數來說,第一個參數是文件名,而第二個參數則是操作文件的方式,"r"代表讀操作,而"w"則代表寫操作。如果沒有指明具體的文件操作方式,那麼默認就是讀操作了。在獲得文件對象log之後,通過readlines方法就可以獲得整個文件所有行的文本內容,並且存放於一個list中,便於進行遍歷。進入到for...in循環體,我們可以見到一個特別的module——re。re是一個用於正則表達式的module,search函數的第一個函數表示正則表達式的pattern,而第二個參數則是用於校驗匹配的字符串。一旦該字符串符合第一個參數給出的pattern,那麼將返回一個MatchObject對象,否則就返回None,所以這樣一個方法就能夠幫助我找出符合條件的字符串了。不過,再仔細想一下,我只不過是要找出包含某個固定子串的字符串而已,出動正則表達式真的有殺雞用牛刀的感覺了。於是,我將if後面的條件判斷換成了string.find(line, "Servlet.Engine.Transports") > 0,並且引入string這個module。這下子總算心裏舒坦了,一個蘿蔔一個坑,簡單的string操作還是用string module比較爽。當然了,速度上也有了提高。好,第一步大功告成,接下來就是對相同文本段進行統計了。
        既然是統計,第一步就要知道在所有的文本段中有多少個是完全不同的,這樣先得到一個不包含重複元素的列表。然後再根據這個列表重新遍歷所有的文本段,得到統計結果。這樣一分析,問題又細化爲如何得到一個不包含重複元素得列表中了(需要說明的一點,對於第一步輸出的文件,我以List嵌套的方式表示之:[[txtline1,txtline2, txtline3], [txtline5, txtline6], ...])。我一開始想到了set這個數據類型,因爲它就是用於表示一個不包含重複元素的數據列表,同時也提供了將list轉化爲set的方法。例如(以下引自Python Tutorial):

None.gif>>> basket = ['apple''orange', 'apple''pear', 'orange', 'banana']
None.gif
>>> fruits = set(basket)            #  create a set without duplicates

None.gif
>>> fruits
None.gifset([
'orange', 'pear', 'apple', 'banana'])


於是,我將set(iterable)這個函數應用到自己構造的List中,卻遇到了這樣的錯誤:TypeError: list objects are unhashable。原來set(iterable)這個函數並不能應用在List的元素也是List的情況下。就這樣,第一個想法被否定了,偶那生鏽的腦袋也短路了,一時間無從下手。後來我想,既然是對List的操作,還是從List入手吧,於是我開始翻閱有關List的函數。很快的,我發現了List有一個count函數,用來統計List中某個元素出現的次數的。這讓我欣喜若狂,因爲solution已經找到了,原來就是這麼簡單:

None.giftmpList  = []
None.gif
for sortedItemList  in sortedList:     # sortedList就是包含了所有文本段的List

None.gif 
     if tmpList.count(sortedItemList) == 0:
None.gif        tmpList.append(sortedItemList)


最後得到的tmpList就是一個不包含重複文本段的List了。有了這樣一個所有元素都是唯一的List,最後的統計也很好辦了。這不是還有剛剛幫了大忙的count函數嗎?

None.gifunique = open("D:\\uniqueList.txt""w")
None.gif
for tmpItemList in
tmpList:
None.gif    
for tmpItem in 
tmpItemList:
None.gif          unique.write(tmpItem)
None.gif     countno 
= sortedList.count(tmpItemList)


好了,大功告成!一個繁瑣的task在Python的幫助下,變得簡單而有趣,而我也不由得被注重實效的Python再次深深打動了。可是,每一次都被打動,而每一次都因爲這樣那樣的藉口把她晾在了一邊,直到有了棘手的task纔想起她,真的很糟糕。要做一名pragmatic programmer,光說不練可不行啊!OK,思過完畢,我們下次再接着聊Python,哈哈!   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章