软件的需求分析

程序处理用户需求的模式为:

  • wc.exe [parameter][filename]

在[parameter]中,用户通过输入参数与程序交互,需实现的功能如下:

1、基本功能

  • 支持 -c  统计文件字符数
  • 支持 -w 统计文件单词数
  • 支持 -l  统计文件总行数

2、拓展功能

  • 支持 -a 返回高级选项(代码行 空行 注释行)
  • 支持 -s 递归处理符合条件的文件

3、高级功能

  • 支持 -x 程序以图形界面与用户交互

[filename] 是待处理文件名。

基本功能的实现

我们先来讨论基本功能的实现,在基本功能中,用户通过输入命令行的方式与程序实现交互。如 wc.exe -c -w -l D:\hello.txt 命令是实现统计 D 盘下 hello.txt 文件的字数、单词数以及总行数;而 wc.exe -l D:\hello.txt 命令是实现统计 D 盘下 hello.txt 文件的总行数;可以发现,用户每次输入的参数个数可能会是不同的,所以我们首先要做的就是从用户的输入命令中分理出命令参数和文件名。

可以用如下代码实现这一功能,在这段代码中,我们将用户输入的字符串截取成字符串数组,数组的最后一位为文件名,其他位的均为命令参数。

 string message = ""; // 存储用户命令
while (message != "exit")
{
Console.Write("wc.exe ");
message = Console.ReadLine(); // 得到输入命令
string[] arrMessSplit = message.Split(' '); // 分割命令
int iMessLength = arrMessSplit.Length;
string[] sParameter = new string[iMessLength-];
// 获取命令参数数组
for (int i=; i< iMessLength-; i++)
{
sParameter[i] = arrMessSplit[i];
}
// 获取文件名
string sFilename = arrMessSplit[iMessLength - ];
}

接下来,我们新建一个类 WC 来对命令进行处理,类的基本框架如下:

 public class WC
{
private string sFilename; // 文件名
private string[] sParameter; // 参数数组
private int iCharcount; // 字符数
private int iWordcount; // 单词数
private int iLinecount; // 总行数 // 参数控制信息
public void Operator(string[] sParameter, string sFilename)
{
}
// 统计基本信息:字符数 单词数 行数
private void BaseCount(string filename)
{
}
// 打印信息
private void Display()
{
}
}

在Operator()方法中,捕捉 "-c"、"-w"、"-l" 命令,通过参数素组的设置调用不同的类方法进行处理;Display()方法用来打印输出信息; BaseCount() 方法用以统计指定文件的字符数、单词数以及总行数。

我们首先填充 Operator() 方法,此方法在参数数组中包含 "-c" "-w" "-l" 时调用 BaseCount() 方法实现文件基本信息的统计,调用 Display() 方法打印结果。代码如下:

 // 控制信息
public void Operator(string[] sParameter, string sFilename)
{
this.sParameter = sParameter;
this.sFilename = sFilename; foreach (string s in sParameter)
{
// 基本功能
else if (s == "-c" || s == "-w" || s == "-l")
{
break;
}
else
{
Console.WriteLine("参数 {0} 不存在", s);
break;
}
}
}

在 BaseCount() 中,通过传入的文件名对文件进行读取。并进行字符数、单词数、总行数的判断。判断规则如下

  • 对文件进行逐字符的读取,每读取一个字符,则字符数加1
  • 在读取到 '\n' 字符时,判定文件总行数加1
  • 在读取到单词分割符时,判定文件单词数加1。单词分割符可以是空格,制表符和各类标点符号
 // 统计基本信息:字符数 单词数 行数
private void BaseCount(string filename)
{
try
{
// 打开文件
FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
StreamReader sr = new StreamReader(file);
int nChar;
int charcount = ;
int wordcount = ;
int linecount = ;
//定义一个字符数组
char[] symbol = { ' ', '\t', ',', '.', '?', '!', ':', ';', '\'', '\"', '\n', '{', '}', '(', ')', '+' ,'-',
'*', '='};
while ((nChar = sr.Read()) != -)
{
charcount++; // 统计字符数 foreach (char c in symbol)
{
if(nChar == (int)c)
{
wordcount++; // 统计单词数
}
}
if (nChar == '\n')
{
linecount++; // 统计行数
}
}
iCharcount = charcount;
iWordcount = wordcount + ;
iLinecount = linecount + ;
sr.Close();
}
catch (IOException ex)
{
Console.WriteLine(ex.Message);
return;
}
}

接下来,我们只需要将统计信息打印出来,就完成了基本功能的实现。

 // 打印信息
private void Display()
{
foreach (string s in sParameter)
{
if (s == "-c")
{
Console.WriteLine("字 符 数:{0}", iCharcount);
}
else if (s == "-w")
{
Console.WriteLine("单 词 数:{0}", iWordcount);
}
else if (s == "-l")
{
Console.WriteLine("总 行 数:{0}", iLinecount);
}
}
Console.WriteLine();
}

扩展功能的实现

在扩展功能中,用户通过输入命令行的方式与程序实现交互。并且在基本功能的基础上新增了2个命令参数;wc.exe -s -c -w D:\*.txt 实现统计 D 盘下所有文件名是txt文件的字符数与单词数的统计。wc.exe -a -c -w D:\hello.txt 实现 D 盘 hello.txt 文件 空行数、代码行数、注释行数以及字符数和单词数的统计。

在这里,我们需要扩展WC类的结构

 public class WC
{
private string sFilename; // 文件名
private string[] sParameter; // 参数数组
private int iCharcount; // 字符数
private int iWordcount; // 单词数
private int iLinecount; // 行 数
private int iNullLinecount; // 空行数
private int iCodeLinecount; // 代码行数
private int iNoteLinecount; // 注释行数 // 初始化
public WC()
{
this.iCharcount = ;
this.iWordcount = ;
this.iLinecount = ;
this.iNullLinecount = ;
this.iCodeLinecount = ;
this.iNoteLinecount = ;
} // 控制信息
public void Operator(string[] sParameter, string sFilename)
{
this.sParameter = sParameter;
this.sFilename = sFilename; foreach (string s in sParameter)
{
// 遍历文件
if (s == "-s")
{
}
// 高级选项
else if (s == "-a")
{
}
// 基本功能
else if (s == "-c" || s == "-w" || s == "-l")
{
...
}
else
{
...
}
}
} // 统计基本信息:字符数 单词数 行数
private void BaseCount(string filename)
{
...
} // 统计高级信息:空行数 代码行数 注释行数
private void SuperCount(string filename)
{
}
// 打印信息
private void Display()
{
...
}
}

修改 Operator() 方法,让其能捕捉到 "-a" 命令,实现高级选项(代码行、空行、注释行的统计),代码如下:

 // 高级选项
else if (s == "-a")
{
Console.WriteLine("文件名:{0}", sFilename);
SuperCount(sFilename);
BaseCount(sFilename);
Display();
break;
}

在 SuperCount() 中,通过传入的文件名对文件进行读取。进行代码行、空行、注释行的统计。判断规则如下:

  • 空行:本行全为空行或是格式控制字符,若包含代码,则只有不超过一个可显示字符,如 "{";
  • 注释行:本行开头除去多余空格或格式控制字符,以 ”//" 开头,或单字符+ "\\" 开头的行;
  • 代码行:除去空行和注释行的其他行。

代码如下

 // 统计高级信息:空行数 代码行数 注释行数
private void SuperCount(string filename)
{
try
{
// 打开文件
FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
StreamReader sr = new StreamReader(file);
String line;
int nulllinecount = ;
int codelinecount = ;
int notelinecount = ;
while ((line = sr.ReadLine()) != null)
{
// 除去每行开头多余空格和格式控制字符
line = line.Trim(' ');
line = line.Trim('\t');
// 空行
if (line == "" || line.Length <= )
{
nulllinecount++;
}
// 注释行
else if(line.Substring(, ) == "//" || line.Substring(, ) == "//")
{
notelinecount++;
}
// 代码行
else
{
codelinecount++;
}
}
iNullLinecount = nulllinecount;
iCodeLinecount = codelinecount;
iNoteLinecount = notelinecount;
sr.Close();
}
catch (IOException ex)
{
Console.WriteLine(ex.Message);
return;
}
}

再次修改 Operator()  方法,让其能捕捉到 "-s" 命令,递归处理符合条件的文件,代码如下:

 // 遍历文件
if (s == "-s")
{
try
{
string[] arrPaths = sFilename.Split('\\');
int pathsLength = arrPaths.Length;
string path = ""; // 获取输入路径
for (int i = ; i < pathsLength - ; i++)
{
arrPaths[i] = arrPaths[i] + '\\'; path += arrPaths[i];
} // 获取通配符
string filename = arrPaths[pathsLength - ]; // 获取符合条件的文件名
string[] files = Directory.GetFiles(path, filename); foreach (string file in files)
{
Console.WriteLine("文件名:{0}", file);
SuperCount(file);
BaseCount(file);
Display();
}
break;
}
catch (IOException ex)
{
Console.WriteLine(ex.Message);
return;
}
}

高级功能的实现

在高级功能中,用户单独输入 "-x" 参数,调用图形界面实现单个文件的选中,并输出文件信息。首先,我们修改主函数,让其可以接受单个 "-x" 命令。

 static void Main(string[] args)
{
string message = ""; while (message != "exit")
{
Console.Write("wc.exe ");
// 得到输入命令
message = Console.ReadLine();
message = message.Trim(' ');
message = message.Trim('\t');
if (message != "-x")
{
// 分割命令
string[] arrMessSplit = message.Split(' ');
int iMessLength = arrMessSplit.Length;
string[] sParameter = new string[iMessLength - ];
// 获取命令参数
for (int i = ; i < iMessLength - ; i++)
{
sParameter[i] = arrMessSplit[i];
}
// 获取文件名
string sFilename = arrMessSplit[iMessLength - ];
// 新建处理类
WC newwc = new WC();
newwc.Operator(sParameter, sFilename);
}
else
{
string[] sParameter = new string[];
sParameter[] = message;
WC newwc = new WC();
newwc.Operator(sParameter, "");
}
}
}

接下来,在 Operator() 函数中增加对于 “-x" 参数的捕捉,实现调用 OpenFileDialog() 类调用文件选择对话框。

 if(s == "-x")
{
string resultFile = "";
OpenFileDialog fd = new OpenFileDialog();
fd.InitialDirectory = "D:\\Patch";
fd.Filter = "All files (*.*)|*.*|txt files (*.txt)|*.txt";
fd.FilterIndex = ;
fd.RestoreDirectory = true;
if (fd.ShowDialog() == DialogResult.OK)
{
resultFile = fd.FileName;
Console.WriteLine("文件名:{0}", resultFile);
SuperCount(resultFile);
BaseCount(resultFile);
DisplayAll();
}
break;
}

注意:在主函数入口前加入 [STAThread] ,否则对话框不会显示。

 class Program
{
[STAThread]
static void Main(string[] args)
{
...
}
}

最后,我们新增 DisplayAll() 函数打印全部信息

 private void DisplayAll()
{
foreach (string s in sParameter)
{
Console.WriteLine("字 符 数:{0}", iCharcount);
Console.WriteLine("单 词 数:{0}", iWordcount);
Console.WriteLine("总 行 数:{0}", iLinecount);
Console.WriteLine("空 行 数:{0}", iNullLinecount);
Console.WriteLine("代码行数:{0}", iCodeLinecount);
Console.WriteLine("注释行数:{0}", iNoteLinecount);
}
Console.WriteLine();
}

至此,我们基本实现了 wc.exe 的基本功能、扩展功能和功能。

用 C# 实现文件信息统计(wc)命令行程序的更多相关文章

  1. wc命令行程序

    软件的需求分析 程序处理用户需求的模式为: wc.exe [parameter][filename] 在[parameter]中,用户通过输入参数与程序交互,需实现的功能如下: 1.基本功能 支持 - ...

  2. 软件工程第三个程序:“WC项目” —— 文件信息统计(Word Count ) 命令行程序

    软件工程第三个程序:“WC项目” —— 文件信息统计(Word Count ) 命令行程序 格式:wc.exe [parameter][filename] 在[parameter]中,用户通过输入参数 ...

  3. MAC与windows下打开当前文件路径的命令行(终端)

    MAC 下文件夹与终端: 1.打开当前路径的终端窗口方法: ①直接拖动要打开的文件夹到终端 ②打开finder的服务偏好设置, 勾选"新建位于文件夹位置的终端"选项, 以后可以在文 ...

  4. 用deamon打开ISO文件,提示命令行错误!!

    用deamon打开ISO文件,提示命令行错误!! 解决方法:(没有关联iso文件)重新卸载deamon,再重新安装,在关联iso文件处打对勾,安装完成后即可用

  5. 在 Mac OS X 上创建的 .NET 命令行程序访问数据库 (使用Entity Framework 7 )

    var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...

  6. 命令行程序增加 GUI 外壳

    Conmajia © 2012 Updated on Feb. 21, 2018 命令行大家都用过: 图 1 命令行程序工作界面 现在想办法为它做一个 GUI 外壳,实际效果参考图 2. 图 2 带 ...

  7. myapp——自动生成小学四则运算题目的命令行程序(侯国鑫 谢嘉帆)

    1.Github项目地址 https://github.com/baiyexing/myapp.git 2.功能要求 题目:实现一个自动生成小学四则运算题目的命令行程序 功能(已全部实现) 使用 -n ...

  8. c#词频统计命令行程序

    这里将用c#写一个关于词频统计的命令行程序. 预计时间分配:输入处理3h.词条排序打印2h.测试3h. 实际时间分配:输入处理1h.词条排序打印2h.测试3h.程序改进优化6h. 下面将讲解程序的完成 ...

  9. Node.js 命令行程序开发资料

    Node.js 命令行程序开发教程http://www.ruanyifeng.com/blog/2015/05/command-line-with-node.html用Node.js创建命令行工具ht ...

  10. Node: 开发命令行程序

    CLI 的全称是 Command-line Interface (命令行界面),即在命令行接受用户的键盘输入并作出响应和执行的程序. 在 Node.js 中,全局安装的包一般都具有命令行界面的功能,例 ...

随机推荐

  1. tp5.1路由报错No input file specified.

    问题: 按照官方教安装了框架,打开首页没问题,可是安装教程路由规则打开 "http://127.0.0.1/hello/2" 时, 却报错误  "No input fil ...

  2. day 23 模块2

    1.namedtuple     命名元组  -> 类似创建一个类 from collections import namedtuple   # 类 p = namedtuple("P ...

  3. CAT 安装运行配置教程

    CAT安装教程 首先安装mysql数据库,具体步骤参阅<mysql免安装教程>--http://www.cnblogs.com/halberts/p/8723938.html 下载CAT代 ...

  4. Android编译命令

    目录 说在前面 编译流程 编译指令 代码编译 代码检索 其他指令 说在前面 从最开始接触Android系统开始,每次进行代码编译都需要网上搜索编译指令.后来大致熟悉了Android的编译体系,加深了对 ...

  5. ...续上文(一个小萌新的C语言之旅)

    我们继续上次没介绍完的继续讲: 下面我们说一下二进制,二进制是计算技术中广泛采用的一种 数制. 二进制数据是用0和1两个 数码来表示的数.它的基数为2,进位规则是“逢二进一”.那么二进制怎么转化为十进 ...

  6. python中自定义超时异常的几种方法

    最近在项目中调用第三方接口时候,经常会出现请求超时的情况,或者参数的问题导致调用异代码异常.针对超时异常,查询了python 相关文档,没有并发现完善的包来根据用户自定义的时间来抛出超时异常的模块.所 ...

  7. python通过mongoengine中connect函数连接多个数据库

    mongoengine支持程序同时连接多个数据库,这些数据库可以位于一个或多个mongo之中,通过alias名称区分不同的连接即可. 可以通过switch_db切换到不同的数据库,进行读写操作,swi ...

  8. Oracle DELETE和TRUNCATE 的区别

    语法delete from aa truncate table aa 区别 1.delete from后面可以写条件,truncate不可以. 2.delete from记录是一条条删的,所删除的每行 ...

  9. nested class 例子

    #include<iostream> using namespace std; /* start of Enclosing class declaration */ class Enclo ...

  10. ADO.NET基础学习-----四种模型,防止SQL注入

    1.ExcuteNonQuery 执行非查询语句,返回受影响的行数. // 1.ExcuteNonQuery string sqlconn = "Data Source=wss;Initia ...