一 引子
相信所有的開發人員都經歷過或正經歷着這樣一個階段:在面對一些編程問題時,總是沒有思路,老是要問別人或百度,不具備自己的編程思維。
筆者認為無論哪門語言,編程中最常用的元素無非是字符串,數組和字典等集合類。遺憾的是大多數的書中只是教我們這些類有哪些方法,每個方法的作用是什么,但是很少講到何種情況下該綜合使用它們。
二 能否獨立優雅地解決此編程任務---是檢驗一名開發人員是否已經初具編程思維的分水嶺
程序將讀取用戶指定的任意文本文件,然后允許用戶從該文件中查找單詞。查詢的結果是該單詞出現的次數,並列出每次出現所在的行。
如果某單詞在同一行中多次出現,程序將只顯示該行一次。行號按升序顯示,即第 1行應該在第 2 行之前輸出,依此類推。
2. 效果如下:
3. 需求整理
本程序的需求如下:
它必須允許用戶指明要處理的文件名字。
程序將存儲該文件的內容,以便輸出每個單詞所在的原始行。
它必須將每一行分解為各個單詞,並記錄每個單詞所在的所有行。
在輸出行號時,應保證以升序輸出,並且不重復。
對特定單詞的查詢將返回出現該單詞的所有行的行號。
輸出某單詞所在的行文本時,程序必須能根據給定的行號從輸入文件中獲取相應的行。
4.數據結構設計
List<string> m_strLines = new List<string>();
Dictionary<string, HashSet<int>> m_dicWord2LineNoSets = new Dictionary<string, HashSet<int>>();
使用一個 List<string> 類型的對象存儲整個輸入文件的副本。
輸入文件的每一行是該 List 對象的一個元素。
因而,在希望輸出某一行時,只需以行號為下標獲取該行所在的元素即可(比如m_strLines[1]代表文本的第二行)。
將每個單詞所在的行號存儲在一個 HashSet 容器對象中。
使用 HashSet 就可確保每行只有一個條目,而且行號將自動按升序排列。
使用一個 Dictionary 容器將每個單詞與一個 HashSet 容器對象關聯起來,該 HashSet 容器對象記錄此單詞所在的行號。
讀取文本文件到流中的代碼如下:
1 if (!File.Exists(@textBox1.Text)) 2 { 3 MessageBox.Show("文件不存在!"); 4 return; 5 } 6 aFile = new FileStream(@textBox1.Text, FileMode.Open); 7 sr = new StreamReader(aFile, Encoding.UTF8);
問題解決方案代碼如下:
1 int iLin = 1; 2 string strLine; 3 strLine = sr.ReadLine();//sr是文件流對象,英文文本文件被讀入其中。 4 //加工原材料(多行的英文文章) 5 //如何加工(解析文章,分解成每一行,對每一行再分解成一個個單詞,最后構造我們設計的數據結構的數據部分) 6 7 8 //循環從文件流中讀取每一行 9 while (strLine != null) 10 { 11 if (strLine != null) 12 { 13 m_strLines.Add(strLine); 14 //去掉標點符號 15 string strRemove = Regex.Replace(strLine, @"[\p{P}*]", " "); 16 //分割出單詞 17 string[] strWordArr = Regex.Split(strRemove, @"\s"); 18 foreach (string strTemp in strWordArr) 19 { 20 if (strTemp.ToLower() != "")//大小寫視為同一單詞 21 { 22 //如果當前單詞不在字典中 23 if (!m_dicWord2LineNoSets.ContainsKey(strTemp.ToLower())) 24 { 25 HashSet<int> linHashSet = new HashSet<int>(); 26 linHashSet.Add(iLin);//初始化行號集合,並且將當前行號做為集合的第一個元素添加進去 27 //將單詞做為key,行號集合(目前只有一個)作為值添加進字典。 28 m_dicWord2LineNoSets.Add(strTemp.ToLower(), linHashSet); 29 } 30 else//如果當前單詞已經在字典中 31 { 32 //對應行號集合添加新行號,由HashSet特性,重復行號不會添加 33 m_dicWord2LineNoSets[strTemp.ToLower()].Add(iLin); 34 } 35 } 36 } 37 } 38 iLin++; 39 strLine = sr.ReadLine(); 40 } 41 sr.Close(); 42 } 43 44 //此時最初的原材料(多行的英文文章)已經被處理成易於使用的結構化數據m_dicWord2LineNoSets 45 //字典里沒有用戶要查找的單詞 46 if (!m_dicWord2LineNoSets.ContainsKey(textBox2.Text.ToLower())) 47 { 48 MessageBox.Show("查無此單詞!"); 49 return; 50 } 51 textBox3.Text = "共有" + m_dicWord2LineNoSets[textBox2.Text].Count + "處!\n"; 52 //字典里有用戶要查找的單詞 53 //查詢m_dicWord2LineNoSets,找到單詞對應的行號集合 54 foreach (int i in m_dicWord2LineNoSets[textBox2.Text]) 55 { 56 //對於行號集合中的每一行,輸出該行對應的文本 57 textBox3.Text = textBox3.Text + "lin\t" + i.ToString() + "\t" + m_strLines[i - 1] + "\n"; 58 }
三 解決編程問題思路總結
由上面問題的解決辦法可以得出,大部分編程問題的解決過程均可歸結如下:
1.分析清楚詳細需求(我們可以獲得的輸入有哪些,要求的輸出是什么);
2.設計合理的數據結構,重新加工我們最初的獲得的輸入(英文文章文本),到半成品(組織良好的數據m_dicWord2LineNoSets);
3.在半成品中給出用戶要求的解。
由此可見數據結構課程在計算機專業中所占的重要地位,遺憾的是,目前大部分高校數據結構教材還在重點講述各種鏈表,堆棧,二叉樹等數據結構的底層實現方法,而忽視了這些數據結構的應用案例講解。
四 CSharpWinformGo開源項目說明
CSharpWinformGo是一個開源的輕量級Winform開發框架,用來展示一些c#中重要知識點的案例(不斷補充新的學習案例),非常適合初學者學習研究,界面如下:
五 源碼位置
代碼托管到SVNChina 中國源代碼托管中心了,大家要下載需要在上面注冊一下用戶(很簡便),SVNChina 中國源代碼托管中心網址http://www.svnchina.com/
使用svn工具checkout以下地址 http://u.svnchina.com/svn/csharpwinformgo
SVN客戶端安裝包下載地址
出處: http://www.cnblogs.com/ice-river/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
正在看本人博客的這位童鞋,我看你氣度不凡,談吐間隱隱有王者之氣,日后必有一番作為!旁邊有“推薦”二字,你就順手把它點了吧,相得准,我分文不收;相不准,你也好回來找我!