這個作業屬於哪個課程 | 班級的連結 |
---|---|
這個作業要求在哪裡 | 作業要求的連結 |
這個作業的目標 | 實現論文查重演算法,並對程式碼進行效能分析、單元測試,使用PSP表 |
GitHub連結
一、PSP表
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | 15 | 20 |
Estimate | 估計這個任務需要多少時間 | 15 | 30 |
Development | 開發 | 150 | 210 |
Analysis | 需求分析 (包括學習新技術) | 30 | 50 |
Design Spec | 生成設計文件 | 60 | 80 |
Design Review | 設計複審 | 20 | 30 |
Coding Standard | 程式碼規範 (為目前的開發制定合適的規範) | 15 | 15 |
Design | 具體設計 | 25 | 20 |
Coding | 具體編碼 | 30 | 150 |
Code Review | 程式碼複審 | 30 | 40 |
Test | 測試(自我測試,修改程式碼,提交修改) | 30 | 50 |
Reporting | 報告 | 40 | 60 |
Test Repor | 測試報告 | 20 | 25 |
Size Measurement | 計算工作量 | 15 | 15 |
Postmortem & Process Improvement Plan | 事後總結,並提出過程改進計劃 | 40 | 40 |
合計 | 535 | 835 |
二、計算模組的設計與實現
1、總體設計
目錄表:
流程圖
2、詳細設計
DataPreprocessing類
read_file 函式:讀取檔案資料,發生異常則丟擲對應的異常並返回-1。
preprocess_data 函式:對資料進行全形轉半形、去除HTML標籤、保留中文字元操作,發生異常則丟擲對應的異常並返回空字元.
to_dbc 函式:對資料進行全形轉半形操作。
SimHashService類
get函式:使用jieba分詞,過濾標點符號或無效字元,過濾標點符號或無效字元,按權重逐位計算雜湊值,將統計結果轉換成0/1字串。
關鍵演算法流程圖!
get_word_hash 函式:計算詞雜湊值。
get_word_weight 函式:定義詞的權重。
hamming_distance 函式:計算漢明距離。
SimilarityChecker類:
check_similarity函式:計算兩篇文章的SimHash後計算漢明距離,根據漢明距離計算論文相似度。
三、效能改進
1、效能分析
這裡我使用PyCharm自帶的Profile功能進行效能分析。
從上圖中可以看到耗時最長的是隻執行一次的Python內建的
2、改進思路
可以增加對生成processed_text1, processed_text2的合理性判斷,減少 checker.check_similarity方法的呼叫,因為get方法耗時很長,而get方法就是checker.check_similarity呼叫的。
四、單元測試
思路:
使用unittest庫,共設計25個函式(分4個類TestDataPreprocessing、TestGetWordHash、TestHammingDistance、TestSimilarityChecker)來對6個關鍵函式進行測試,儘可能地對我所構建的論文查重演算法進行測試,保證程式碼健壯性、準確性、通用性。
資料預處理單元測試程式碼展示:
def test_preprocess_data_with_valid_input(self):
"""測試 preprocess_data 方法,包含 HTML 標籤和非中文字元"""
text_content = "<html>這是一個測試! 123</html>"
result = DataPreprocessing.preprocess_data(text_content)
expected = "這是一個測試" # 去除HTML標籤和非中文字元後的結果
self.assertEqual(result, expected)
def test_preprocess_data_with_empty_input(self):
"""測試 preprocess_data 方法,輸入為空字串"""
text_content = ""
result = DataPreprocessing.preprocess_data(text_content)
self.assertEqual(result, "")
def test_preprocess_data_with_all_non_chinese(self):
"""測試 preprocess_data 方法,輸入不包含中文字元"""
text_content = "Hello, World! 123"
result = DataPreprocessing.preprocess_data(text_content)
self.assertEqual(result, "")
def test_to_dbc_with_full_width_characters(self):
"""測試 to_dbc 方法,將全形字元轉換為半形"""
input_str = "ABCabc123!@#"
result = DataPreprocessing.to_dbc(input_str)
expected = "ABCabc123!@#"
self.assertEqual(result, expected)
def test_to_dbc_with_mixed_characters(self):
"""測試 to_dbc 方法,混合全形和半形字元"""
input_str = "Hello ABC"
result = DataPreprocessing.to_dbc(input_str)
expected = "Hello ABC"
self.assertEqual(result, expected)
def test_read_file(self):
"""測試 read_file 方法"""
file_path = "D:\PyCharmCode\SimHash\\3122004446\DataSet\測試用例\DataPreprocessing.txt"
result = DataPreprocessing.read_file(file_path)
expected = "《軟 件工程!,123"
self.assertEqual(result, expected)
def test_read_file_err1(self):
"""測試 read_file 方法 不存在次檔案"""
file_path = "D:\PyCharmCode\SimHash\\3122004446\DataSet\測試用例\DataPreprocessing_err1.txt"
result = DataPreprocessing.read_file(file_path)
# print(f"result7:{result}")
expected = -1
self.assertEqual(result, expected)
單元測試結果截圖
覆蓋率
這裡使用Python的coverage庫進行分析。
程式碼檔案覆蓋率截圖:
各個函式覆蓋率截圖:
各個類覆蓋率截圖:
五、異常處理
這裡我用unittest庫對各個單元進行異常測試。
異常一
read_file函式異常:在讀取檔案時找不到目標檔案或無法正常開啟目標檔案時,異常時丟擲異常提示並返回-1。
測試樣例:
def test_read_file_err1(self):
"""測試 read_file 方法 不存在次檔案"""
file_path = "D:\PyCharmCode\SimHash\\3122004446\DataSet\測試用例\DataPreprocessing_err1.txt"
result = DataPreprocessing.read_file(file_path)
# print(f"result7:{result}")
expected = -1
self.assertEqual(result, expected)
結果截圖:
異常二
to_dbc函式異常:當非字串輸入、非常大的字串、特殊字元、外部環境問題、編碼問題時,異常時丟擲異常提示並返回輸入內容。
測試樣例:
class TestDataPreprocessing(unittest.TestCase):
def test_to_dbc_with_mixed_characters(self):
"""測試 to_dbc 方法,混合全形和半形字元"""
input_str = None
result = DataPreprocessing.to_dbc(input_str)
expected = None
self.assertEqual(result, expected)
結果截圖:
異常三
get_word_hash函式:當非字串輸入、索引越界、特殊字元、編碼問題、極端長度的字串時,異常時丟擲異常提示並返回0。
測試樣例:
class TestGetWordHash(unittest.TestCase):
def test_empty_string(self):
# 測試空字串輸入
result = SimHashService.get_word_hash(123)
self.assertEqual(result, BIGINT_0)
結果截圖:
異常四
get_word_weight函式:當輸入數字時,丟擲異常提示並返回0。
測試樣例:
class TestGetWordHash(unittest.TestCase):
def test_empty_string(self):
# 測試空字串輸入
result = SimHashService.get_word_weight(111)
self.assertEqual(result, BIGINT_0)
結果截圖:
異常五
hamming_distance函式:當輸入的值不是字串時,丟擲異常提示並返回-1。
測試樣例:
class TestHammingDistance(unittest.TestCase):
def test_both_none(self):
# 測試兩個輸入都為 None
result = SimHashService.hamming_distance("110", 110)
self.assertEqual(result, -1)
結果截圖:
六、總結
這是我第一次按照PSP表寫的程式,從前期的預估安排時間、學習新知識,到期間的編寫程式,再到後期測試程式與撰寫報告。體會到了PSP表對於提高軟體開發人員個人生產效率和產品質量的作用,也接觸到了unittest、coverage、profile等測試分析的方法,是一次難忘的程式設計經理。