一、项目简介
源码地址:https://gitee.com/Juice-Dreamer/WordCount.git
1.项目需求:对程序设计语言源文件统计字符数、单词数、行数,统计结果以指定格式输出到默认文件中,以及其他扩展功能,并能够快速地处理多个文件。
wc.exe -c file.c //返回文件 file.c 的字符数
wc.exe -w file.c //返回文件 file.c 的单词总数
wc.exe -l file.c //返回文件 file.c 的总行数,
wc.exe -o outputFile.txt //将结果输出到指定文件outputFile.txt
wc.exe -s //递归处理目录下符合条件的文件
wc.exe -a file.c //返回更复杂的数据(代码行 / 空行 / 注释行)
wc.exe -x //该参数单独使用,如果命令行有该参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、单词数、行数等全部统计信息
2.开发环境:vs2017+c#实现
3.思路分析: 从基本功能可以看出这个程序是对文件进行操作,统计一些重要的数据,以及对结果进行处理。扩展功能是对一些高级数据统计,以及快速处理等功能。c#中主要用到的类有FileStream、StreamWriter、StreamReader三个来对文件进行操作,利用这三个类很容易对文件进程操作。我自己在设计时由于没有注重代码可维护性,因此代码很乱,将会在后续慢慢更正。
二、项目实现
1.设计:整个项目设计了一个大类WC,其中的字段和方法如下所示:
private List<string> inputPara = null; //输入参数 private int charCount=0;//需要统计的字符数 private int wordCount = 0;//需要统计的单词数 private int lineCount = 0;//需要统计的总行数 private string inputFile="";//需要处理的文件 private int codeCount = 0;//代码行数 private int emptyCount = 0;//空行 private int noteCount = 0;//注释行 public void analyzeInput(List<string>lstInput)//解析输入,对用户输入的结果进行解析 public void FunBaseCount()//统计基本功能结果 public int WordCount()//统计单词数,因单词数要求很多,拿出来专门作为一个函数进行处理 public void FunSeniorCount()//统计高级数据:空行、代码行、注释行 public void ResultToFile(string file)//将结果输入到指定文件中 输出到默认文件中result.txt public void DisplayAllResult(string file)//统计所有信息到file中
2.实现:
Main函数为程序的入口,进行与用户交互。
[STAThread] static void Main(string[] args) { //程序入口处 try { Console.WriteLine("请输入您对程序的操作,若需多项操作,请用空格隔开!,操作末尾默认是需要操作的文件"); string inputMessage = ""; while (inputMessage != "exit") { Console.Write("wc.exe "); inputMessage = Console.ReadLine(); inputMessage = inputMessage.Trim(' ');//去掉前后空格 inputMessage = inputMessage.Trim('\t'); if (inputMessage != "-x") { string[] input = inputMessage.Split(' '); List<string> inputPara = new List<string>(); for (int i = 0; i < input.Length; i++) { if (input[i] != "") { inputPara.Add(input[i]); } } inputPara = inputPara.Distinct().ToList();//去重复命令 WC wc = new WC(); wc.analyzeInput(inputPara); } else { List<string> inputPara = new List<string>(); inputPara.Add("-x"); inputPara.Add(""); WC wc = new WC(); wc.analyzeInput(inputPara); } } } catch (Exception e) { Console.WriteLine("程序遇到错误:"+e.Message); } }
analyzeInput()主要是对用户输入进行分析来做出判断。
public void analyzeInput(List<string>lstInput)//解析输入,对用户输入的结果进行解析 { this.inputPara = lstInput;//获取参数 inputFile = inputPara[inputPara.Count - 1]; //获取文件 for (int i = 0; i < inputPara.Count - 1;i++ ) { if (inputPara[i] == "-x")//图形化处理 { try { OpenFileDialog ofd = new OpenFileDialog(); ofd.InitialDirectory = Application.StartupPath; ofd.Filter = ".c files(*.c)|*.c"; ofd.FilterIndex = 2; ofd.RestoreDirectory = true; if (ofd.ShowDialog() == DialogResult.OK) { this.inputFile = ofd.FileName; FunBaseCount(); FunSeniorCount(); DisplayAllResult("result.txt"); } } catch (Exception e) { Console.WriteLine(e.Message); } } else if (inputPara[i] == "-c" || inputPara[i] == "-l" || inputPara[i] == "-w")//统计基本功能 { FunBaseCount(); if (!inputPara.Contains("-o")) { ResultToFile("result.txt"); } if (inputPara[i + 1] == "-o")//输出结果到指定文件夹中 { ResultToFile(inputPara[i + 2]); } } else if (inputPara[i] == "-a")//统计高级功能并输入到指定文件夹中 { FunSeniorCount(); if (!inputPara.Contains("-o")) { ResultToFile("result.txt"); } } else if (inputPara[i] == "-s")//遍历当前目录文件夹以及子文件,满足.c文件都可以统计 { try { // 获取符合条件的文件名 string[] files = Directory.GetFiles(@"..\Debug", "*.c",SearchOption.AllDirectories); foreach (string file in files) { // Console.WriteLine(Application.StartupPath + file.Substring(8)); this.inputFile = Application.StartupPath + file.Substring(8); FunBaseCount(); FunSeniorCount(); DisplayAllResult("result.txt"); } break; } catch (IOException ex) { Console.WriteLine(ex.Message); } }
else
{
Console.Write(inputPara[i] + "不是正确的命令,请重新输入!");
} } }
FunBaseCount()统计基本结果。
public void FunBaseCount()//统计基本功能结果 { // Console.Write(inputFile); FileStream fs = new FileStream(inputFile, FileMode.Open,FileAccess.Read);//读取文件 StreamReader sr = new StreamReader(fs); int iChar; int charCount = 0; int lineCount = 0; while ((iChar = sr.Read()) !=-1) { if (iChar != '\n') { charCount++;//字符数 // Console.WriteLine((char)iChar); } if (iChar == '\n') { lineCount++;//行数 } } this.charCount = charCount; this.lineCount = lineCount+1; this.wordCount = WordCount(); fs.Close(); sr.Close(); }
public int WordCount()//统计单词数 { FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read); StreamReader sr = new StreamReader(fs); string readLine = ""; int count = 0; while ((readLine = sr.ReadLine()) != null)//逐行读取 { if (readLine.Trim(' ').Contains(' ')||readLine.Trim(' ').Contains(','))//对含有多个空格等进行分割 { string[] words = readLine.Trim(' ').Split(",' '".ToCharArray()); foreach (string i in words) { if (i.Length != 0) count++; } // Console.WriteLine(i+" len:"+i.Length); } else { // Console.WriteLine("No:" + readLine); count++; } } sr.Close(); fs.Close(); return count; }
FunseniorCount()统计高级计数结果。
public void FunSeniorCount()//统计高级数据:空行、代码行、注释行 { FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read); StreamReader sr = new StreamReader(fs); string readLine; while ((readLine = sr.ReadLine()) != null)//逐行读取 { if (readLine.Trim(' ').Length == 0 || readLine.Trim(' ') == "{" || readLine.Trim(' ') == "}")//空行 { this.emptyCount++; } else if (readLine.Trim(' ').Length > 1)//代码行 { this.codeCount++; } else if ((readLine.Contains("{") || readLine.Contains("}")) && (readLine.Contains("//")))//注释行 { this.noteCount++; } } fs.Close(); sr.Close(); }
ResultToFile()是把统计信息写入指定文件
public void ResultToFile(string file)//将结果输入到指定文件中 输出到默认文件中result.txt { try { FileStream fs = new FileStream(file, FileMode.Create, FileAccess.Write); StreamWriter sw = new StreamWriter(fs); string resultStr = ""; foreach (string s in inputPara)//根据参数输入来决定文件中写什么内容 { if (s == "-c") resultStr = resultStr + inputFile + ",字符数:" + charCount.ToString() + "\r\n"; else if (s == "-l") resultStr = resultStr + inputFile + ",行数:" + lineCount.ToString() + "\r\n"; else if (s == "-w") resultStr = resultStr + inputFile + ",单词数:" + wordCount.ToString() + "\r\n"; else if (s == "-a") { resultStr = resultStr+inputFile + ",代码行:" + codeCount.ToString() + "\r\n"; resultStr = resultStr +inputFile+ ",空行:" + emptyCount.ToString() + "\r\n"; resultStr = resultStr+inputFile + ",注释行:" + noteCount.ToString() + "\r\n"; } } sw.Write(resultStr); sw.Close(); fs.Close(); } catch (IOException e) { Console.WriteLine("结果写入遇到错误:" + e.Message); } }
DisplayAllResult()显示所有统计信息到指定文件中
public void DisplayAllResult(string file)//统计所有信息到file中 { try { FileStream fs = new FileStream(file, FileMode.Create, FileAccess.Write); StreamWriter sw = new StreamWriter(fs); string resultStr = ""; resultStr = resultStr + inputFile + ",字符数:" + charCount.ToString() + "\r\n"; resultStr = resultStr + inputFile + ",行数:" + lineCount.ToString() + "\r\n"; resultStr = resultStr + inputFile + ",单词数:" + wordCount.ToString() + "\r\n"; resultStr = resultStr + inputFile + ",代码行/空行/注释行:" + codeCount.ToString()+"/"+ emptyCount.ToString()+"/"+ noteCount.ToString() + "\r\n"; sw.Write(resultStr); sw.Close(); fs.Close(); } catch (Exception e) { Console.WriteLine("发生错误:"+e.Message); } }
3.测试:
3.1采用白盒测试,对输入进行测试如下图:
test.c内容:
测试-c:
测试-l:
测试-w:
测试-c -l:
测试-c -w:
测试-c -w -l:
错误输入:
测试-s:
测试-x:
测试-o:
3.2单元测试:
创建单元测试
using WordCount; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; namespace TestProject2 { /// <summary> ///这是 WCTest 的测试类,旨在 ///包含所有 WCTest 单元测试 ///</summary> [TestClass()] public class WCTest { private TestContext testContextInstance; /// <summary> ///获取或设置测试上下文,上下文提供 ///有关当前测试运行及其功能的信息。 ///</summary> public TestContext TestContext { get { return testContextInstance; } set { testContextInstance = value; } } #region 附加测试特性 // //编写测试时,还可使用以下特性: // //使用 ClassInitialize 在运行类中的第一个测试前先运行代码 //[ClassInitialize()] //public static void MyClassInitialize(TestContext testContext) //{ //} // //使用 ClassCleanup 在运行完类中的所有测试后再运行代码 //[ClassCleanup()] //public static void MyClassCleanup() //{ //} // //使用 TestInitialize 在运行每个测试前先运行代码 //[TestInitialize()] //public void MyTestInitialize() //{ //} // //使用 TestCleanup 在运行完每个测试后运行代码 //[TestCleanup()] //public void MyTestCleanup() //{ //} // #endregion /// <summary> ///WordCount 的测试 ///</summary> [TestMethod()] public void WordCountTest() { WC target = new WC(); // TODO: 初始化为适当的值 int expected = 10; // TODO: 初始化为适当的值 int actual; actual = target.WordCount(); Assert.AreEqual(expected, actual); Assert.Inconclusive("验证此测试方法的正确性。"); } /// <summary> ///WC 构造函数 的测试 ///</summary> [TestMethod()] public void WCConstructorTest() { WC target = new WC(); Assert.Inconclusive("TODO: 实现用来验证目标的代码"); } /// <summary> ///DisplayAllResult 的测试 ///</summary> [TestMethod()] public void DisplayAllResultTest() { WC target = new WC(); // TODO: 初始化为适当的值 string file = string.Empty; // TODO: 初始化为适当的值 target.DisplayAllResult(file); Assert.Inconclusive("无法验证不返回值的方法。"); } /// <summary> ///FunBaseCount 的测试 ///</summary> [TestMethod()] public void FunBaseCountTest() { WC target = new WC(); // TODO: 初始化为适当的值 target.FunBaseCount(); Assert.Inconclusive("无法验证不返回值的方法。"); } /// <summary> ///FunSeniorCount 的测试 ///</summary> [TestMethod()] public void FunSeniorCountTest() { WC target = new WC(); // TODO: 初始化为适当的值 target.FunSeniorCount(); Assert.Inconclusive("无法验证不返回值的方法。"); } /// <summary> ///ResultToFile 的测试 ///</summary> [TestMethod()] public void ResultToFileTest() { WC target = new WC(); // TODO: 初始化为适当的值 string file = string.Empty; // TODO: 初始化为适当的值 target.ResultToFile(file); Assert.Inconclusive("无法验证不返回值的方法。"); } /// <summary> ///analyzeInput 的测试 ///</summary> [TestMethod()] public void analyzeInputTest() { WC target = new WC(); // TODO: 初始化为适当的值 List<string> lstInput = null; // TODO: 初始化为适当的值 target.analyzeInput(lstInput); Assert.Inconclusive("无法验证不返回值的方法。"); } /// <summary> ///WordCount 的测试 ///</summary> [TestMethod()] public void WordCountTest1() { WC target = new WC(); // TODO: 初始化为适当的值 int expected = 0; // TODO: 初始化为适当的值 int actual; actual = target.WordCount(); Assert.AreEqual(expected, actual); Assert.Inconclusive("验证此测试方法的正确性。"); } } }
测试结果:
3.3静态代码检查:工具是StyleCode
注:项目中也存在一些问题,这里就不一一列举,会再进行重新设计,争取完成一个更好的版本。
三、项目总结
1.总结:
本次实验是对文件进行数据统计的一个小项目。在编程中发现,如果没有理解需求是根本很难完成代码编写的;其次,如果不进行结构设计,也很难完成。因此我自己会再对这个项目进行优化,重新设计结构,争取形成一个稳定运行的版本。
从这次小项目中,可以看出来自己存在的很多严重的不足,往后还需更加注重代码维护性与代码设计方面,争取能够对代码进行优化,让自己有所收获。
2.致谢:
本作业中内容参考和借鉴华中科技大学wujianjie老师发布的作业https://www.cnblogs.com/ningjing-zhiyuan/p/8654132.html在此表示感谢。