剛開始學習PYTHON,感覺到這個語言真的是很好用,可以快速完成功能實現。
最近試着用它完成工作中的一個任務:在Linux服務器中完成對.xml.gz文件的解析,生成.csv文件,以供SqlServer服務器導入,做進一步的數據分析。
解壓后的xml文件格式如下:
<?xml version="1.0" encoding="UTF-8"?> <bulkPmMrDataFile> <fileHeader fileFormatVersion="V1.0.4" reportTime="2016-02-22T21:00:00.000" ></fileHeader> <eNB id="122941"> <measurement> <smr>MR.LteScRSRP MR.LteScRSRQ MR.LteScTadv MR.LteSceNBRxTxTimeDiff ...</smr> <object id="31472897" MmeUeS1apId="2417300878" MmeGroupId="1025" MmeCode="199" TimeStamp="2016-02-22T20:00:02.517"> <v>38 22 6 31 34 0 34 38400 110 NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL 7 23 NIL 26 24 0 0 </v> </object> <object id="31472897" MmeUeS1apId="2416229278" MmeGroupId="1025" MmeCode="199" TimeStamp="2016-02-22T20:00:02.792"> <v>61 30 5 32 45 0 27 38400 110 NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL 7 14 NIL 1 49 0 0 </v> </object> <object id="31472897" MmeUeS1apId="2148286566" MmeGroupId="1025" MmeCode="199" TimeStamp="2016-02-22T20:00:07.177"> <v>42 26 5 31 30 412 35 38400 110 NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL 7 14 NIL 26 24 0 0 </v> </object> <object id="31472897" MmeUeS1apId="2417300878" MmeGroupId="1025" MmeCode="199" TimeStamp="2016-02-22T20:00:07.677"> <v>39 22 7 31 35 387 36 38400 110 36 18 38400 108 NIL NIL NIL NIL NIL NIL NIL 5 10 NIL 25 25 0 0 </v> </object> <object id="31472897" MmeUeS1apId="2416229278" MmeGroupId="1025" MmeCode="199" TimeStamp="2016-02-22T20:00:07.962"> <v>61 27 5 31 43 427 36 38400 110 49 4 38400 108 NIL NIL NIL NIL NIL NIL NIL 9 13 NIL 1 49 0 0 </v> <v>61 27 5 31 43 427 36 38400 110 46 0 38400 295 NIL NIL NIL NIL NIL NIL NIL 9 13 NIL 1 49 0 0 </v> </object> <object id="31472897" MmeUeS1apId="269973066" MmeGroupId="1025" MmeCode="199" TimeStamp="2016-02-22T20:00:08.382"> <v>41 23 9 32 23 447 36 38400 110 32 11 38400 358 NIL NIL NIL NIL NIL NIL NIL 6 22 NIL 24 26 0 0 </v> <v>41 23 9 32 23 447 36 38400 110 31 7 38400 295 NIL NIL NIL NIL NIL NIL NIL 6 22 NIL 24 26 0 0 </v> <v>41 23 9 32 23 447 36 38400 110 28 8 38400 460 NIL NIL NIL NIL NIL NIL NIL 6 22 NIL 24 26 0 0 </v> </object> </measurement> <measurement> <smr>MR.LteScPlrULQci1 MR.LteScPlrULQci2 MR.LteScPlrULQci3 MR.LteScPlrULQci4 ...</smr> <object id="60057088" MmeUeS1apId="2416982066" MmeGroupId="1025" MmeCode="206" TimeStamp="2016-02-24T06:00:03.962"> <v>NIL NIL NIL NIL NIL NIL NIL NIL 0 NIL NIL NIL NIL NIL NIL NIL NIL 0 </v> </object> .......................... </measurement> <measurement> <smr>MR.LteScRIP</smr> <object id="60057089:37900:2" TimeStamp="2016-02-24T06:00:11.259" MmeCode="NIL" MmeGroupId="NIL" MmeUeS1apId="NIL"> <v>81</v> </object> .......................... </measurement> </eNB> </bulkPmMrDataFile>
從廖雪峰博客學習得知目前常用的兩種xml解析方式為DOM和SAX,分別進行了嘗試,從個人角度來看,還是DOM比較好用,包括應用邏輯以及代碼編寫,都相對輕松簡單。
在嘗試SAX解析的過程中,開始按照習慣的方式,對XML文件中篩選的可用字段進行字符串相加,導致的后果是程序運行特別的慢,於是又嘗試了cStringIO內存文件,這才感受到了內存文件的速度優勢。
下面列出其中的關鍵字段以作對比。
- 使用字符串相加方式(s為定義的字符串變量):
#處理開始標簽 def start_element(self, name, attrs): global d_eNB global d_obj global s if name == 'eNB': d_eNB = attrs elif name == 'object': d_obj = attrs elif name == 'v': s = s + d_eNB['id']+' '+ d_obj['id']+' '+d_obj['MmeUeS1apId']+' '+d_obj['MmeGroupId']+' '+d_obj['MmeCode']+' '+d_obj['TimeStamp']+' ' else: pass
- 使用cStringIO內存文件方式(output為定義的cStringIO變量):
#處理開始標簽 def start_element(self, name, attrs): global d_eNB global d_obj if name == 'eNB': d_eNB = attrs elif name == 'object': d_obj = attrs elif name == 'v': output.write(d_eNB['id']+' '+ d_obj['id']+' '+d_obj['MmeUeS1apId']+' '+d_obj['MmeGroupId']+' '+d_obj['MmeCode']+' '+d_obj['TimeStamp']+' ') else: pass
對於一個相同的.xml.gz文件,文件總行數為173890行,其中有用數據為90079行。實測結果如下:
可以看到,在應用了內存文件后,程序的處理性能有了翻天覆地的變化,處理耗時為前者的將近千分之一,贊!!!
最近又嘗試修改字符串連接為list-join方式,代碼如下:
- 使用list-join方式(lst為定義的list變量):
#處理開始標簽 def start_element(self, name, attrs): global d_eNB global d_obj global lst if name == 'eNB': d_eNB = attrs elif name == 'object': d_obj = attrs elif name == 'v': lst.append(d_eNB['id']+' ') lst.append(d_obj['id']+' ') lst.append(d_obj['MmeUeS1apId']+' ') lst.append(d_obj['MmeGroupId']+' ') lst.append(d_obj['MmeCode']+' ') lst.append(d_obj['TimeStamp']+' ') #列表連接 t.writelines(''.join(lst).replace(' \n','\r\n').replace(' ',',').replace('T',' ').replace('NIL','')) #寫入解析后內容
對於一個相同的.xml.gz文件,文件總行數為173890行,其中有用數據為90079行。實測結果如下:
可以看到,在應用了list-join方式后,處理用時和StringIO方式相當,說明利用list-join方式也能夠帶來和StringIO相同的性能提升。
因此,推薦在編程中要避免使用過多的字符串相加,而要以list-join和StringIO代替之。
至於為什么字符串相加效率要比其他兩種低那么多呢,其實我也不是很懂,但是我會搜索啊。。。
誰說python字符串相加效率低
這個看情況分析,官方文檔當中也有說,相加產生的str是immutable的,如果只是兩個字符串相加,並沒有什么問題,但是如果是n>>1個字符串相加,這樣中間會產生n-1個中間值,這些中間值都是immutable的,所以之后就是創建一個釋放一個再創建下一個釋放下一個。
而join在對於n個字符串相加過程中內部實現直接全部相連,就沒有這種中間值了。
如果你相加的字符串不多,用加號還是更加方便的,另一方面,你選擇了用Python,還真的在乎那一兩秒的效率嗎?
測試用的程序和文件可以到我的github頁面下載,歡迎小伙伴們一起學習討論。
xml_parser_str.py、xml_parser_list.py、xml_parser_StringIO.py
測試用例:../mro