https://github.com/linzhihuang2000/031902510
一、PSP表格
(2.1)在開始實現程序之前,在附錄提供PSP表格記錄下你估計將在程序的各個模塊的開發上耗費的時間。
(2.2)在你實現完程序之后,在附錄提供的PSP表格記錄下你在程序的各個模塊的開發上實際花費的時間。
PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
---|---|---|---|
Planning | 計划 | ||
· Estimate | · 估計這個任務需要多少時間 | 60 | 90 |
Development | 開發 | ||
· Analysis | · 需求分析 (包括學習新技術) | 480 | 450 |
· Design Spec | · 生成設計文檔 | 60 | 90 |
· Design Review | · 設計復審 | 30 | 10 |
· Coding Standard | · 代碼規范 (為目前的開發制定合適的規范) | 10 | 20 |
· Design | · 具體設計 | 60 | 40 |
· Coding | · 具體編碼 | 1000 | 1020 |
· Code Review | · 代碼復審 | 20 | 30 |
· Test | · 測試(自我測試,修改代碼,提交修改) | 20 | 60 |
Reporting | 報告 | ||
· Test Repor | · 測試報告 | 60 | 90 |
· Size Measurement | · 計算工作量 | 10 | 5 |
· Postmortem & Process Improvement Plan | · 事后總結, 並提出過程改進計划 | 20 | 10 |
· 合計 | 1830 | 1915 |
二、計算機模塊接口
(3.1)計算模塊接口的設計與實現過程。設計包括代碼如何組織,比如會有幾個類,幾個函數,他們之間關系如何,關鍵函數是否需要畫出流程圖?說明你的算法的關鍵(不必列出源代碼),以及獨到之處。(18')
3.1.1類和函數(三個類,15個函數)
- SensitivewordFilter類:
- SensitivewordFilter函數,調用SensitiveWordInit類用DFA算法,把敏感詞庫寫入一個Map
- CheckSensitiveWord2函數:主要實現文本查詢敏感詞的函數
- Checkstring函數:檢測該純英文+特殊字符的字符串是不是被轉換為拼音的敏感詞
- initialization函數:因為是一行一行讀入文本,所以每次都要初始化
- ishan函數:判斷是否為漢字
- pinyin函數:把敏感詞庫的漢字,轉換為拼音,並用一個Map實現拼音對應漢字
- hanzitopinyin函數:把該漢字,轉換為拼音
- splitci()函數:調用splitcihui類的函數,實現漢字左右結構拆分,並用一個Map實現左右結構的兩個漢字對應組成的漢字(弓雖對應強)
- sum()函數:答案匯總
- readfile()函數:讀入敏感詞庫
- readtext()函數:讀入文本
- outflie函數:輸出文件
- SensitiveWordInit類:
- initKeyWord函數:調用addSensitiveWordToHashMap函數
- addSensitiveWordToHashMap函數:用DFA算法,把敏感詞庫寫入一個Map
- splitcihui類
- splitci()函數:把左右結構的漢字記錄起來,存入一個Map中
3.1.2算法關鍵之處:
- CheckSensitiveWord2函數和Checkstring函數。 基本思想CheckSensitiveWord2函數(遞歸)檢測到一個字符是敏感詞(這里用敏感詞fun舉例,假設文本是fuN),檢測到fu,發現下個N不是n,則把N改為n,再次調用CheckSensitiveWord2函數
-
CheckSensitiveWord2函數:文本一個字符一個字符的讀,若讀到特殊字符:直接讀下一個字符;若是字母:則把它存在一個字符串中,為Checkstring函數作准備; 開始檢測,如果成功,繼續檢測,若失敗,找失敗原因(字母大小寫?還是諧音問題?還是左右結構問題),符合失敗原因,就把當前的字替換一下(例如N替換為n),繼續調用該函數看看能不能成功檢測到敏感詞最后一個字。 若檢測到敏感詞最后一個字,則再次調用該函數,但是檢測的文本要去掉剛剛檢測過的文本
-
Checkstring函數:讀一段只有純英文+特殊字符的字符串,把它轉換為漢字,去檢測敏感詞,看看能否檢測成功。
-
3.1.3獨到之處
- 我自認為沒有獨到之處。如果硬要說獨到之處,就是用遞歸的思想,把各種情況實現吧,比如說,文本有F,它可以是用f,法,發.....等等詞匯去檢測
(3.2)計算模塊接口部分的性能改進。記錄在改進計算模塊性能上所花費的時間,描述你改進的思路,並展示一張性能分析圖(由VS 2019、JProfiler或者Jetbrains系列IDE自帶的Profiler的性能分析工具自動生成),並展示你程序中消耗最大的函數。
3.2.1性能改進
- 剛開始我只是用一個for循環,當檢測不成功的時候(替換一個字符),則i--(把文本下標前移),但是做到一半的時候,我發現這只適用,一個字符只能替換的字只有一種的情況。要是要解決諧音敏感詞時,就不能用了,畢竟諧音對應好幾個(例如:你,尼,擬,泥,妮)。
- 解決方法:於是乎,我想出了用遞歸的思想,當檢測不成功時,判斷當前的字,是否是被替換的,如果不是,並且他可以替換,就帶着替換的字和當前參數,再次調用這個尋找敏感詞的函數。
3.2.2性能分析圖
- 可以看出,除了導入拼音包,查找文本中的敏感詞的函數花費的時間最多。代碼如下
public static int CheckSensitiveWord2(String txt,int j,int n,Map nowMap, String sensitiveci, String nowstring, boolean flag,String ti){
//從左到右的參數是,文本,文本已經讀到的下標,文本是第幾行,當前敏感詞庫,
// 當前已經讀到的敏感詞庫中的詞,當前讀到的敏感詞,是否被替換false表示沒有替換
//當前替換的詞
String word="";
int start=-1;//一串純英文加特殊符號在txt的下標
int end=0;
for(int i = j; i < txt.length() ; i++) {
if (!flag)
word = txt.substring(i, i + 1);
else
word=ti;//true表示當前檢測的,是被替換的
if (regEx.indexOf(word) != -1) {//特殊字符直接跳過
if (!nowstring.equals(""))
nowstring += word;//nowstring不為空,說明當前已找到敏感詞的部分內容
continue;
}
if(!flag)
{
if(moderncase.indexOf(word)!=-1)//是不是字母
{
if(string.equals("")) {
start = i;
tempstringMap=nowMap;
tempstringnowstring=nowstring;
tempstringsensitiveci=sensitiveci; //把當前參數存起來,為找 變成拼音 的敏感詞
}
string+=word;
}
//當檢測到漢字或是最后一個字符,string(一串純英文)去檢測拼音敏感詞
if((ishan(word)||i==txt.length()-1)&&!string.equals("")&&start!=-1)
{
if(i==txt.length()-1&&moderncase.indexOf(word)!=-1)
end=i;
else
end=i-1;
if(Checkstring(txt,start,end,n)==1)//檢測拼音敏感詞
{
//返回值為1,說明這串英語字符,是拼音敏感詞
nowMap=tempstringMap;
nowstring=tempstringnowstring+txt.substring(start,end+1);
sensitiveci=tempstringsensitiveci;
}
string="";
}
}
Map tempMap = nowMap;
nowMap = (Map) nowMap.get(word);//獲取指定key
if (nowMap != null) {//存在,則判斷是否為最后一個
if (!flag)
nowstring += word;
sensitiveci += word;
flag = false;
if ("1".equals(nowMap.get("isEnd"))) {//如果為最后一個匹配規則,結束循環,返回匹配標識數
total++;
an += "Line" + n + ":" + " <" + sensitiveci + "> " + nowstring + "\n";
//初始化
nowMap = sensitiveWordMap;
sensitiveci="";
nowstring="";
string="";
flag=false;
ti="";
//找完找下一個
CheckSensitiveWord2(txt,i+1,n,nowMap,sensitiveci,nowstring,flag,ti);
return 1;
}
}
//該字符是英文,但是檢測不成功
else if(moderncase.indexOf(word)!=-1&&!flag)
{
int pan=2;
String tempstring=nowstring;
if(uppercase.indexOf(word)!=-1)//是小寫
{
flag = true;
nowstring += word;
word = word.toLowerCase();//改為大寫再次檢測
ti=word;
nowMap = tempMap;
pan=CheckSensitiveWord2(txt,i,n,nowMap,sensitiveci,nowstring,flag,ti);
}
else//大寫的話,改為小寫去檢測
{
flag = true;
nowstring += word;
word = word.toUpperCase();
ti=word;
nowMap = tempMap;
pan=CheckSensitiveWord2(txt,i,n,nowMap,sensitiveci,nowstring,flag,ti);
}
if(pan==1) {//pan為1,說明被替換的字母正是我們要找的
string = "";
return 1;
}
//否則,就初始化
sensitiveci = "";
nowstring = "";
nowMap = sensitiveWordMap;
flag = false;
}
//字符是漢字,但是檢測不成功,可能是諧音,或者左右結構拆開的原因(強=弓雖)
else if(ishan(word) && !flag)
{
int pan=2;//用來判斷的
String word2;
if (i != txt.length() - 1)//左右結構拆開
{
word2 = txt.substring(i + 1, i + 2);
if (ishan(word2)) {
String word3 = word;
word3 += word2;
if (split.containsKey(word3)) {//如果改字是被拆開的,那把他組合起來再去檢測
flag = true;
nowstring += word3;
word = split.get(word3);
nowMap = tempMap;
pan=CheckSensitiveWord2(txt,i+1,n,nowMap,sensitiveci,nowstring,flag,word);
}
if(pan==1)//pan為1,說明組合起來的字去檢測是成功的
return 1;
}
}
//諧音問題
String wordpinyin=hanzitopinyin(word);//把該字轉換為拼音
if(pinyintohanzi.containsKey(wordpinyin))//如果該拼音的確是敏感詞
{
String temp=pinyintohanzi.get(wordpinyin);//一串是諧音的敏感詞字符串,例如(fa),該字符串就是發法罰....
flag = true;
nowstring += word;
nowMap = tempMap;
for (int z=0;z<temp.length();z++)//一個一個諧音去試
{
word=temp.substring(z,z+1);
pan=CheckSensitiveWord2(txt,i,n,nowMap,sensitiveci,nowstring,flag,word);
if(pan==1)
return 1;
}
}
sensitiveci = "";
nowstring = "";
nowMap = sensitiveWordMap;
flag = false;
}
//該字符不能被替換,或已經被替換過了
else {
if(flag)
return -1;//-1表示替換了,也不成功
sensitiveci = "";
nowstring = "";
nowMap = sensitiveWordMap;
flag = false;
}
}
return 2;//2表示程序正常結束
}
(3.3)計算模塊部分單元測試展示。展示出項目部分單元測試代碼,並說明測試的函數,構造測試數據的思路。並將單元測試得到的測試覆蓋率截圖,發表在博客中。(12')
- 1.測試能否用DFA方法建立樹
public static void main(String[] args) {
long beginTime = System.currentTimeMillis();
Set<String> set1 = new HashSet<String>();
splitci();
set1 = readfile("D:\\testwords.txt");//讀入敏感詞文件
SensitivewordFilter filter = new SensitivewordFilter(set1);//把敏感詞用DFA寫入Map
long endTime = System.currentTimeMillis();
System.out.println("總共消耗時間為:" + (endTime - beginTime)+"毫秒");
}
- testword.txt文件包含(笨蛋和笨逼)兩個敏感詞結果如下
- 測試覆蓋率如下
- 2.測試檢測文本中的敏感詞匯,測試思路:判定為敏感詞的要求(中文:插字符,諧音,拼音,拼音首字母,左右結構。英語:插字符,大小寫)
public static void main(String[] args) {
long beginTime = System.currentTimeMillis();
Set<String> set1 = new HashSet<String>();
splitci();
set1 = readfile("D:\\testwords.txt");//讀入敏感詞文件
SensitivewordFilter filter = new SensitivewordFilter(set1);//把敏感詞用DFA寫入Map
readtext("D:\\testorg.txt");
sum();//把答案匯總;
outflie("D:\\testans.txt");//輸出文件
long endTime = System.currentTimeMillis();
System.out.println("總共消耗時間為:" + (endTime - beginTime)+"毫秒");
}
-
敏感詞(hello,山寨,愛過,強奸)和檢測文本
-
結果如下
-
測試覆蓋率如下
(3.4)計算模塊部分異常處理說明。在博客中詳細介紹每種異常的設計目標。每種異常都要選擇一個單元測試樣例發布在博客中,並指明錯誤對應的場景。(6')
IOError: 當輸入文件不存在,輸出找不到該文件
- 單元測試代碼:
public static void readfile(String readf) {
try {
File f = new File(readf);//指定文件
FileInputStream fis = new FileInputStream(f);//創建輸入流fis並以f為參數
InputStreamReader isr = new InputStreamReader(fis,"UTF-8");//創建字符輸入流對象isr並以fis為參數
BufferedReader br = new BufferedReader(isr);//創建一個帶緩沖的輸入流對象br,並以isr為參數
} catch (Exception e) {
System.out.println("找不到該文件");
e.printStackTrace();
}
}
- 沒有文件的情況:
三、心得
(4.1)在完成本次作業過程的心得體會。
- 通過本次學習,我發現了自己有很多的不足之處,從頭到尾都在學習與借鑒,從最基本的java如何輸入輸出文件,到那些JProfile性能分析工具怎么安裝使用。基本上無時無刻在學習,深深地感覺到了自己的不足。
- 也當然學會了很多知識,邊寫代碼邊找到bug,讓我學會了很多解決方法。也了解了如何使用一些工具等等。
- 最最重要的是,當前還是無法完全解決拼音帶來的問題,在很多情況下還是檢測不出來。平心而論,已經盡力了,但能力有限,只能今后加倍提升自己。如果有解決的方法或者本人代碼不足之處,歡迎留言告知。
- 再補充一點,看到大佬們提交的作業,也發現了應該用,各種情況的笛卡爾積去建樹(DFA),但截止時間將至,只好有空的時候對大佬們提交的作業進行學習借鑒。