一 引子
相信所有的开发人员都经历过或正经历着这样一个阶段:在面对一些编程问题时,总是没有思路,老是要问别人或百度,不具备自己的编程思维。
笔者认为无论哪门语言,编程中最常用的元素无非是字符串,数组和字典等集合类。遗憾的是大多数的书中只是教我们这些类有哪些方法,每个方法的作用是什么,但是很少讲到何种情况下该综合使用它们。
二 能否独立优雅地解决此编程任务---是检验一名开发人员是否已经初具编程思维的分水岭
程序将读取用户指定的任意文本文件,然后允许用户从该文件中查找单词。查询的结果是该单词出现的次数,并列出每次出现所在的行。
如果某单词在同一行中多次出现,程序将只显示该行一次。行号按升序显示,即第 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 + "处! "; 52 //字典里有用户要查找的单词 53 //查询m_dicWord2LineNoSets,找到单词对应的行号集合 54 foreach (int i in m_dicWord2LineNoSets[textBox2.Text]) 55 { 56 //对于行号集合中的每一行,输出该行对应的文本 57 textBox3.Text = textBox3.Text + "lin " + i.ToString() + " " + m_strLines[i - 1] + " "; 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/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我!