要求
- 对源文件(*.txt,*.cpp,*.h,*.cs,*.html,*.js,*.java,*.py,*.php等)统计字符数、单词数、行数、词频,统计结果以指定格式输出到默认文件中,以及其他扩展功能,并能够快速地处理多个文件。
- 使用性能测试工具进行分析,找到性能的瓶颈并改进
- 对代码进行质量分析,消除所有警告
- 设计10个测试样例用于测试,确保程序正常运行(例如:空文件,只包含一个词的文件,只有一行的文件,典型文件等等)
- 使用Github进行代码管理
- 撰写博客
基本功能
- 统计文件的字符数
- 统计文件的单词总数
- 统计文件的总行数
- 统计文件中各单词的出现次数
- 对给定文件夹及其递归子文件夹下的所有文件进行统计
- 统计两个单词(词组)在一起的频率,输出频率最高的前10个。
在Linux系统下,进行性能分析,过程写到blog中
首先给出PSP表格:
项目进度 预计耗时 实际耗时
分析题目,确定大体思路 1h 2.5h
(包括编程工具,语言等)
实现单个文件中字符,行数,单词的读取 1h 3h
由于vs2017临时崩盘,改换vs2013并 0h 2.5h
重新配置开发环境
实现遍历根目录下所有文件 3h 7h
实现对单词数目的统计 3h 10h
实现对词组的统计 5h 6h
整体测试 10h …
总计 23h 31h
由于周末一直在忙着写作业,直到周一晚上才开始着手进行代码的实现,首先来分析一下各部分的完成情况:
1、 对单个文件字符,行数,单词数的统计。
这个应该是最容易完成的项目了,直接通过对文件的单个字符进行读取和判断,即可实现,由于秉承单元测试的想法,在完成和测试上一共花费约2.5h。(周一晚)
2、 由于以前一直使用devil C 进行编程,首次使用vs十分不习惯,由于电脑是windows 7系统,无法进行预处理命令,于是悲摧的又下载了vs2013,而vs2013居然没有汉化包,只能通过英文版进行调试。在网上查了很久才搞清楚预处理命令在哪里改。
3、 接下来要实现的是遍历所有文件,花了2个多小时在网上搜了四五种代码,却发现没有一个是可以真正拿来用的,不是文件打开失败就是只能读取目录下的第一个文件。
只知道使用 firstfile函数打开,在经历了数小时尝试无果的情况下问了同小组的成员,于是得到了一下模板:
void GetAllFiles(string path, vector<string>& files)
{
long handle = 0; // File Handle
struct _finddata_t fileinfo; // File Information
string p;
char * location; // location converts char * into string
if ((handle = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
{
do
{
if ((fileinfo.attrib & _A_SUBDIR)) // to check whether it is a folder or a file
{
if (strcmp( fileinfo.name, ".") != 0 && strcmp( fileinfo.name, "..") != 0)
{
files.push_back(p.assign(path).append("\\").append( fileinfo.name));
GetAllFiles(p.assign(path).append("\\").append( fileinfo.name), files);
}
}
else
{
files.push_back(p.assign(path).append("\\").append( fileinfo.name));
location = (char *)p.data();
//printf("%s\n", fileinfo.name);
//OpenFile(location);
//getchar();
}
} while (_findnext(handle, &fileinfo) == 0);
_findclose(handle);
}
}
看了一下之后发现基本没有会用的,百度之后才明白vector是一个存放文件名的容器。在此函数的基础上进行改动最终实现文件夹的遍历。
4、 满心欢喜的写完了文件遍历的代码,将老师给的测试集打开进行整个文件夹的字符数,行数,单词数的统计,结果运行到一半时就崩了,一脸无奈的进行调试后发现是由于某些文件打开失败,大概有二十个左右。最后同组的同学告诉我可能是上面的那个函数有一行写的不合理,将那一行注释掉了之后问题依然没有完全解决,然而时间以经过去两天了。
5、 心情复杂的开始写单词的统计,刚开始的时候居然毫无思路,自己最初的想法是遍历所有字符,遇到连着4个以上的单词就存入结构体中,并创建链表。而实际操作过程中却发现一是这样遍历不好统计没个单词的数目,二是时间消耗非常严重。经同组同学提醒,应当使用哈希表来进行存储。而哈希表的知识我已经基本遗忘,于是花了大约1.5h补一下哈希表的内容。
6、 发现C本身自带的map根本不会使用,于是自己开始写哈希表,而此时DDL只有不到半天,勉强写好了哈希函数,放到全是文本文件的文件夹中测试时直接崩掉。由于对VS的DEBUG功能极其不熟悉,导致很久也没能找出问题所在。于是不得已进行逐行调试,在经历大约2小时的测试过程后终于发现是由于部分文件打开失败造成的,在全部换成可打开的文件后却发现统计的单词和数目根本不对,而DDL只剩下2个小时了,于是心态爆炸的写了命令行参数和输出文件后提交了。
7、 由于基本功能中的词组统计依然没有实现,因此没有进行性能分析和优化处理,也没有进行Linux系统下的分析。
经验总结:
1、 对VS开发环境极其不熟悉,不知道如何进行有效的DEBUG
2、 对于基本的数据结构知识有所遗忘,比如在创建链表的过程中出现了很多BUG,在打开文件时出现了打开失败的情况。
3、 不能及时的于其他人进行交流,一味的埋头苦弄,导致效率极其低下,比如说其实只需2小时就可以解决的文件遍历问题居然做了整整一天。
最后,附上已经实现功能的代码:
// ConsoleApplication4.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<io.h>
#include"targetver.h"
#include<fstream>
#include<vector>
#include<iostream>
#include<tchar.h>
#include<numeric>
#include<functional>
#include<unordered_map>
using namespace std;
int char_num = 0, line_num = 1, word_num = 0;
#define lnode struct node
struct node {
int num;
char word[200];
struct node* next;
};
void GetAllFiles(string path, vector<string>& files,int &char_num,int &line_num,int &word_num)
{
FILE *f1 =NULL;
int count = 0, i = 0;
long handle = 0; // File Handle
struct _finddata_t fileinfo; // File Information
string p;
char * location; // location converts char * into string
char code;
if ((handle = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
{
do
{
if ((fileinfo.attrib & _A_SUBDIR)) // to check whether it is a folder or a file
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
{
//files.push_back(p.assign(path).append("\\").append(fileinfo.name));
GetAllFiles(p.assign(path).append("\\").append(fileinfo.name), files, char_num, line_num, word_num);
}
}
else
{
files.push_back(p.assign(path).append("\\").append(fileinfo.name));
location = (char *)p.data();
f1 = fopen(location, "r");
if (!f1) printf("%s\n", location);
if (f1) {
while ((code = fgetc(f1)) != EOF){
if (code >= 32 && code <= 126) char_num++;
if (code == '\n')
line_num++;
if ((code >= 65 && code <= 90) || (code >= 97 && code <= 122)){
count++;
}
else {
if (count >= 4){
word_num++;
count = 0;
}
else count = 0;
}
}
}
}
} while (_findnext(handle, &fileinfo) == 0);
_findclose(handle);
}
}
int main(){
FILE *f2;
char * DATA_DIR = __argv[1], *s1 = "characters:",*s2="lines:",*s3="words:";
vector < string > files;
GetAllFiles(DATA_DIR, files, char_num, line_num, word_num);
printf("%d %d %d\n", char_num, line_num, word_num);
f2 = fopen("result.txt", "w");
if (!f2) printf("ERROR");
if (f2) {
fputs(s1, f2);
fprintf(f2,"%d",char_num);
fputc('\n', f2);
fputs(s2, f2);
fprintf(f2, "%d", line_num);
fputc('\n', f2);
fputs(s3, f2);
fprintf(f2, "%d", word_num);
}
system("pause");
return 0;
}