軟工實踐第一次個人編程作業


這個作業屬於哪個課程 http://dwz.date/cts4
這個作業要求在哪里 https://edu.cnblogs.com/campus/fzu/SE2020/homework/11167
這個作業的目標 解析json 熟悉命令行操作 規范編程習慣 學習測試項目 github使用
學號 031802516
語言 python

PSP 表格

PSP2.1 Personal Software Process Stages 預估耗時(分鍾) 實際耗時(分鍾)
Planning 計划 60 90
Estimate 估計這個任務需要多少時間 10 20
Development 開發
Analysis 需求分析 (包括學習新技術) 40 60
Design Spec 生成設計文檔 15 15
Design Review 設計復審 10 10
Coding Standard 代碼規范 (為目前的開發制定合適的規范) 10 10
Design 具體設計 40 40
Coding 具體編碼 500 300
Code Review 代碼復審 100 60
Test 測試(自我測試,修改代碼,提交修改) 100 200
Reporting 報告 40 30
Test Report 測試報告 30 20
Size Measurement 計算工作量 15 15
Postmortem & Process Improvement Plan 事后總結, 並提出過程改進計划 60 90
合計 1030 960

解題思路

剛開始看到題目的時候有點懵,看到控制台命令有點不知所措,在舍友的示范下很快就明白了。多次閱讀題目后就有了比較清晰的思路,其實這題就是解析 json。但是由於要處理的數據量很大,所有要在初次訪問的時候就把數據保存在本地起到緩存作用,並且在讀文件時也采用readline() 防止內存炸掉。然后就開始復習了 json 的格式、python里一些數據結構之間的轉換以及正則表達式的規則,解析完json就是完成了內核的,但是要編寫命令提供交互,然后又去查詢了有關py命令行參數編寫的方法。

設計實現過程

代碼組織

抓住這個編程題的核心就是解析json, 於是就先編寫解析一個json文件的函數JsonAnalyse() 提取有用的信息, 在把JsonAnalyse 函數裝進另一個尋找json文件的函數AnalyseAll(),實現對目標目錄下所有的json文件的解析。把這些函數和相關的數據分裝在類 Data里,在初始化init 的時候調用這些函數,解析並把所的數據以json的格式(解析采用 正則表達式 )存在本地,在后續的查詢時直接調用本地文件,起到緩存的作用,最后再編寫下命令行接口就好了。

核心代碼init函數流程圖:

代碼說明

思路

先編寫好解析一個json文件的函數,把要用的信息提取(采用正則表達式法 )出來並保存在類里定義的變量里,再把這個函數裝進一個搜索文件夾中存在.json文件的函數就形成了一個解析一個文件夾的函數,最后配合一個把數據保存本地的函數,在初始化(構造)結束時就完成了所有解析。二次訪問的時候就是把保存的文件提取出來,進行查詢操作(查詢函數過於簡單就不再貼出來了,本質就是 json 和 dict 之間的轉換)。整體上來說就是從核心突破,化繁為簡,完成核心功能函數再一步步拓展,就可以完成整個的項目。

TotalAnalyse 函數

def TotalAnalyse(self,addr:str):#解析文件夾中的所有json
        for root, dic, files in os.walk(addr):
            for f in files:
                if f[-5:] == ".json":
                    jsonPath = f
                    self.JsonAnalyse(addr, jsonPath)        

 

JsonAnalyse 函數

    def JsonAnalyse(self, addr:str,jPath:str):#單個json解析函數
        f = open(addr + '\\' + jPath, 'r', encoding='utf-8')
        try:
            while True:
                stmp = f.readline()
                if stmp:
                    pattern = re.compile('"login":".*?",')
                    res=pattern.search(stmp).group(0)
                    rkey1 = res[9:-2]

                    pattern = re.compile('"name":".*?",')
                    res = pattern.search(stmp).group(0)
                    rkey2 = res[8:-2]

                    pattern = re.compile('"type":".*?",')
                    res = pattern.search(stmp).group(0)
                    rkey3 = res[8:-2]

                    if not rkey3 in ['PushEvent','IssueCommentEvent',
                                            'IssuesEvent','PullRequestEvent']:
                        continue
                    if not rkey1 in self.uEvent.keys():
                        event = {'PushEvent':0,'IssueCommentEvent':0,
                                 'IssuesEvent':0,'PullRequestEvent':0}
                        self.uEvent[rkey1] = event

                    if not rkey2 in self.rEvent.keys():
                        event = {'PushEvent':0,'IssueCommentEvent':0,
                                 'IssuesEvent':0,'PullRequestEvent':0}
                        self.rEvent[rkey2] = event

                    if not rkey1+'&'+rkey2 in self.urEvent.keys():
                        event = {'PushEvent': 0, 'IssueCommentEvent': 0,
                                 'IssuesEvent': 0, 'PullRequestEvent': 0}
                        self.urEvent[rkey1 + '&' + rkey2] = event
                    self.uEvent[rkey1][rkey3] += 1
                    self.rEvent[rkey2][rkey3] += 1
                    self.urEvent[rkey1 + '&' + rkey2][rkey3] += 1
                else:
                    break
        except:
            pass
        finally:
            f.close()

SaveToLocal 函數

def SaveToLocal(self):
        try:
            with open('user.json', 'w', encoding = 'utf-8') as f:
                json.dump(self.uEvent, f)
            with open('repo.json', 'w', encoding = 'utf-8') as f:
                json.dump(self.rEvent, f)
            with open('userepo.json', 'w', encoding = 'utf-8') as f:
                json.dump(self.urEvent, f)
        except:
            raise RuntimeError("save error")
        finally:
            f.close()    

單元測試截圖和描述

單元測試概述

分別對Data類測試初始化(包含了json 的讀取、解析以及寫入)、 三個查詢(QueryByUser,QueryByRepo,QueryByUserAndRepo),在.json 文件插入幾條特定的數據,在測試中采用斷言的方式來判斷是否成功完成對應的操作。一共4個test函數全部通過。

單元測試代碼截圖

單元測試運行截圖

單元測試覆蓋率

單元測試覆蓋率優化和性能測試

性能測試提升

由於python 雞肋的多線程,導致性能提升胎死腹中,不得不感嘆一句 java 才是永遠的神。
既然無法在多線程下手,就要從解析單個json突破。原來解析json模式為 讀取文件->字符串->json -> json讀取,改成了讀取文件->字符串->正則表達式 模式。不僅可以加快索引的時間,也可以省區 str 與 dict 轉換的時間。 經測試,在讀取141MB文件時新模式用時1.29s,而舊模式用時2.19s ;在讀取 1 GB 文件的情況下 新模式用時 5.36s 而舊模式用時 13.07s;可知,新模式的性能相對與舊模式提升了60%以上,並且讀取的文件越大提升的性能就會越顯著。在10GB 文件下,程序測試仍然會運行91.75s, 還是不沒有達到理想要求。
值得一提的是,助教提供的程序有很多重復的json值訪問,只要把這些值先預存起來也可以在一定程度上提高程序的性能。

  • 舊模式 VS 新模式(141MB文件)

  • 舊模式 VS 新模式(1GB文件)

性能測試截圖

代碼規范的鏈接

https://github.com/jieblue/2020-personal-python/blob/master/codestyle.md

總結

這次作業代碼的兩個核心內容解析json 和命令行參數編寫,由於以前有做過解析json的作業,所以沒有碰到太大的問題,命令行參數編寫是第一次碰到,有去查閱資料,也參考了助教提供的模板,最后編寫成功。還有也是第一冊接觸到單元測試和性能測試,也是查閱了一些資料(游走於csdn 和 cnblog之間)才完成這些測試。這次作業學到了很多新的知識,也復習了以前學的一些知識,收獲很多。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM