前言
Github地址:https://github.com/starmiku/xiangmu_wc
编程语言:C
项目简介
wc.exe 是一个常见的工具,它能统计文本文件的字符数、单词数和行数。这个项目要求写一个命令行程序,模仿已有wc.exe 的功能,并加以扩充,给出某程序设计语言源文件的字符数、单词数和行数。
实现一个统计程序,它能正确统计程序文件中的字符数、单词数、行数,以及还具备其他扩展功能,并能够快速地处理多个文件。
基本功能列表:
wc.exe -c file.c
//返回文件 file.c 的字符数 (完成)wc.exe -w file.c
//返回文件 file.c 的词的数目 (完成)wc.exe -l file.c
//返回文件 file.c 的行数 (完成)
扩展功能:
-s
递归处理目录下符合条件的文件未 (未完成)-a
返回更复杂的数据(代码行 / 空行 / 注释行)(完成)
高级功能:
-x
这个参数单独使用。如果命令行有这个参数,则程序会显示图形界面,用户可以通过界面选取单个文件,程序就会显示文件的字符数、行数等全部统计信息。 (未完成)
PSP
PSP2.1 | 预计耗时 | 实际耗时 |
---|---|---|
总体计划 | 2h | 2.5h |
预计完成 | 10h | 12h |
程序开发 | 8h | 10h |
需求分析 | 0.5h | 1h |
设计文档 | 0.5h | 0.5h |
设计复审 | 0.5h | 0.5h |
代码规范 | 0.5h | 0.5h |
具体设计 | 1h | 1h |
具体编码 | 6h | 8h |
代码复审 | 1h | 1h |
程序测试 | 2h | 2h |
程序报告 | 1h | 1h |
测试报告 | 1h | 0.5h |
计算工作量 | 0.5h | 0.5h |
事后总结 | 2.5h | 2h |
总计时间 | 12h | 14.5h |
解题思路
命令行操作,想到通过C的命令行参数实现。
行数计算,通过读取'\n'实现。
字符计算,通过逐一读取字符并排除'\n'和eof实现。
词数计算,通过读取连续的字母(包括符号.和_)实现。
空行计算,通过两个换行符之间是否存在字符实现。
注释行计算,通过读取第一个字符为/进入判断,读取第二个字符为*或者/实现。
代码行计算,排除空行和注释行之外的即为代码行。
程序流程
程序由主函数和五个功能函数组成。其中test()函数为测试函数,保留作为调试窗口。
关键代码
主函数
通过直接读取命令行参数作为程序执行参数,使用while循环提供其他功能的选择。
int main(int argc, char *argv[])
{
int choose = 0;
char com; //获取具体命令
FILE * fp = NULL;
char* func = (char*)malloc(sizeof(char) * 50); //接收命令参数
if (argv[2] != NULL && argv[1] != NULL)
{
fp = fopen(argv[2], "r");
func = argv[1];
com = func[1];
if (fp == NULL)
{
printf("文件打开失败。\n");
exit(1);
}
}
else
{
printf("参数输入错误。\n");
return 0;
}
if (com == 'c')
choose = 1;
else if (com == 'w')
choose = 2;
else if (com == 'l')
choose = 3;
else
choose = 101;
while (1)
{
switch (choose)
{
case 1:
GetLetter(fp);
rewind(fp);
break;
case 2:
GetWord(fp);
rewind(fp);
break;
case 3:
GetLine(fp);
rewind(fp);
break;
case 4:
MoreData(fp);
rewind(fp);
break;
case 100:
fclose(fp);
free(func);
exit(0);
case 101:
printf("错误输入\n\n");
while (getchar() != '\n') //清空所有输入
continue;
break;
}
printf("输入:q 退出\t w 获取词数\t l 获取行数\t c 获取字符数\t a 获取更多信息\t");
scanf("%c", &com);
getchar();
if (com == 'c')
choose = 1;
else if (com == 'w')
choose = 2;
else if (com == 'l')
choose = 3;
else if (com == 'a')
choose = 4;
else if (com == 'q')
choose = 100;
else
choose = 101;
}
//test();
return 0;
}
功能:获取行数
通过读取'\n'(即ASCII码 10)作为换行标记,如果代码行中出现'\n',fgetc()会分别读取到\和n,不会视作换行。
void GetLine(FILE *f)
{
int letter = 0, line = 0;
//接收字符,行数
int mark = 0;
while (!feof(f))
{
letter = fgetc(f);
if (letter == '\n')
line++;
}
printf("共有行数 %d\n\n", line);
}
功能:获取词数
如果由连续的字母和'_'则视为词语,如果前后均为符号或空格,单个字母也视作词语。
void GetWord(FILE *f)
{
int letter = 0, word = 0;
//接收字符,词数
int mark = 0;
//0为非字符,1为字符串
while (!feof(f))
{
letter = fgetc(f);
if ((letter >= 'a'&&letter <= 'z') || (letter >= 'A'&&letter <= 'Z') || letter == '_')
{
if (mark == 0)
mark = 1;
}
else if (letter == '.') //防止头文件和浮点数的'.'被误判
{
if (mark == 0 || mark == 1)
continue;
}
else
{
if (mark == 1)
{
word++;
mark = 0;
}
else
continue;
}
}
printf("共有词数 %d\n\n", word);
}
功能:获取字符数
将一切能读取的字符均计入字符数,除换行符和eof标志。
void GetLetter(FILE *f)
{
int letter = 0, num = 0;
//接收字符,字符数
while (!feof(f))
{
letter = fgetc(f);
if (letter == '\n') //除换行标记外均视为字符
continue;
num++;
}
printf("共有字符数 %d\n\n", num - 1); //除去eof标志
}
拓展:更多数据
先判定是否为空行,如果不为空行则从接收的字符判定为代码行或注释行,通过独立的while循环来读取整行数据,减少判断次数。
void MoreData(FILE *f)
{
int letter = 0, code = 0, empty = 0, note = 0, mark = 0, notemark = 0;
/*
接受字符,代码行数,空行数,注释行数,类型标记,注释行标记
mark的参数:0空行,1存在格式化字符的空行,2代码行,3注释行
notemark参数:0未判定,1单行注释,2多行注释
*/
while (!feof(f))
{
letter = fgetc(f);
if (mark == 0 || mark == 1) //判定空行
{
if (letter == ' ')
continue;
else if (letter == '\n')
{
empty++;
mark = 0;
continue;
}
else if ((letter == '{' || letter == '}') && mark == 0)
{
mark = 1;
continue;
}
else
{
if (letter == '/')
mark = 3;
else
mark = 2;
}
}
if (mark == 2) //判定代码行
{
while (!feof(f) && letter != '\n')
letter = fgetc(f);
mark = 0;
code++;
continue;
}
if (mark == 3) //判定注释行
{
while (!feof(f))
{
letter = fgetc(f);
if (letter == '/' && notemark == 0) //单行注释起始判定
notemark = 1;
else if (letter == '*' && notemark == 0) //多行注释起始判定
notemark = 2;
else if (letter == '*' && notemark == 2) //多行注释结束判定
notemark = 3;
if (notemark == 1) //单行注释结束判定
{
while (!feof(f) && letter != '\n')
letter = fgetc(f);
note++;
mark = 0;
notemark = 0;
break;
}
if (notemark == 2) //多行注释中间行数计算
{
while (!feof(f) && letter != '\n')
letter = fgetc(f);
note++;
}
if (notemark == 3 && letter == '/') //多行注释结束判定
{
while (!feof(f) && letter != '\n')
letter = fgetc(f);
note++;
mark = 0;
notemark = 0;
break;
}
else if (notemark == 3 && letter != '*') //判定是否为注释中的*
notemark = 2;
}
}
}
printf("共有空行数 %d 代码行数 %d 注释行数 %d\n", empty, code - 1, note); //除去eof行
}
测试运行
测试代码
#include<stdio.h>
int main()
{
}
//test
/*
testfile
*/
测试截图