• 能否优雅解决此编程任务是检验一名开发人员是否已经初具编程思维的分水岭


    一 引子

    相信所有的开发人员都经历过或正经历着这样一个阶段:在面对一些编程问题时,总是没有思路,老是要问别人或百度,不具备自己的编程思维。

    笔者认为无论哪门语言,编程中最常用的元素无非是字符串,数组和字典等集合类。遗憾的是大多数的书中只是教我们这些类有哪些方法,每个方法的作用是什么,但是很少讲到何种情况下该综合使用它们。

    能否独立优雅地解决此编程任务---是检验一名开发人员是否已经初具编程思维的分水岭

    1. 任务说明

      程序将读取用户指定的任意文本文件,然后允许用户从该文件中查找单词。查询的结果是该单词出现的次数,并列出每次出现所在的行。
      如果某单词在同一行中多次出现,程序将只显示该行一次。行号按升序显示,即第 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/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
    正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我!
  • 相关阅读:
    读 Zepto 源码之内部方法
    读Zepto源码之代码结构
    vue-auto-focus: 控制自动聚焦行为的 vue 指令
    vue-lazy-render: 延迟渲染大组件,增强页面切换流畅度
    用vue实现模态框组件
    谷歌插件Image downloader开发之popup
    关于const
    Python线程指南(转自AstralWind)
    PyQt中的图形绘制
    sizeof和strlen之间的区别
  • 原文地址:https://www.cnblogs.com/ice-river/p/3567359.html
Copyright © 2020-2023  润新知