Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions 221801227/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/Debug/
/*.sln
/*.vcxproj
/*.filters
/*.user
/.vs
190 changes: 190 additions & 0 deletions 221801227/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
[TOC]

# 题目要求

实现一个命令行程序,不妨称之为**WordCount**。

## 1、实现基本需求

假设有一个软件每隔一小段时间会记录一次用户的搜索记录,记录为英文。

输入文件和输出文件以命令行参数传入。例如我们在命令行窗口(cmd)中输入:

```java
//C语言类
WordCount.exe input.txt output.txt

//Java语言
java WordCount input.txt output.txt
```

则会统计input.txt中的以下几个指标

1. **统计文件的字符数(对应输出第一行)**:

- 只需要统计Ascii码,汉字不需考虑
- 空格,水平制表符,换行符,均**算**字符

2. 统计文件的**单词总数(对应输出第二行)**,单词:至少以**4**个英文字母开头,跟上字母数字符号,单词以分隔符分割,**不区分大小写**。

- 英文字母: A-Z,a-z
- 字母数字符号:A-Z, a-z,0-9
- 分割符:空格,非字母数字符号
- **例**:file123是一个单词, 123file不是一个单词。file,File和FILE是同一个单词

3. 统计文件的**有效行数(对应输出第三行)**:任何包含**非空白**字符的行,都需要统计。

4. 统计文件中各**单词的出现次数(对应输出接下来10行)**,最终只输出频率最高的**10**个。

- 频率相同的单词,优先输出字典序靠前的单词。

> 例如,windows95,windows98和windows2000同时出现时,则先输出windows2000

- 输出的单词统一为小写格式

然后将统计结果输出到output.txt,输出的格式如下;其中`word1`和`word2` 对应具体的单词,`number`为统计出的个数;换行使用'\n',编码统一使用UTF-8。

```
characters: number
words: number
lines: number
word1: number
word2: number
...
```

## 2、接口封装

如果现在我们要把这个功能放到不同的环境中去(例如,命令行,Windows图形界面程序,网页程序,手机App),就会碰到困难:代码散落在各个函数中,很难剥离出来作为一个独立的模块运行以满足不同的需求。

这些代码的种类不同,混杂在一起对于后期的维护扩展很不友好,所以它们的组织结构就需要精心的整理和优化。

我们希望把基本功能里的:

1. 统计字符数
2. 统计单词数
3. 统计最多的10个单词及其词频

这三个功能独立出来,成为一个独立的模块(class library, DLL, 或其它),这样的话,命令行和GUI的程序都能使用同一份代码。为了方便起见,我们称之为计算核心"**Core模块**",这个模块至少可以在几个地方使用:

1. 命令行测试程序使用
2. 在单元测试框架下使用
3. 与数据可视化部分结合使用

把计算核心在单元测试框架中做过完备的测试后,我们就可以在算法层级保证了这个模块的正确性。
但我们知道软件并非只有计算核心,实际的软件是交付给最终用户的软件,除了计算核心外,还需要有一定的界面和必要的辅助功能。

这个Core模块和使用它的其他模块之间则要通过一定的API来交流。
API应该怎么设计呢?
为了方便起见,我们可以从下面的最简单的接口开始(仅举例,你的代码里可能没有这个函数):

```c++
int countChar(File *file)
```

这个函数表示输出一个文件指针,返回这个文件的字符数。
假设我们用Core封装了这个接口,那么我们的测试程序可以是这样:

```c++
File *in = fopen("input.txt","r");
int count = 100;
Assert(countChar(in) == count);
```

当然,这样的测试程序并不充分,希望大家测试时不要像这样偷懒。

## 3、单元测试和性能分析

请根据自己以往积累的测试经验,在编码完成之后,提交产品之前,设计测试用例,并编写单元测试,对自己的项目进行测试。首先,至少应采用白盒测试用例设计方法来设计测试用例,其他测试方法不限。其次,要设计至少10个测试用例,确保你的程序能够正确处理各种情况。最后,结合测试评估的要求,对自己的测试设计进行评价,这些测试用例能满足该程序测试的要求吗?

另一个重要的措施是要把单元测试自动化,这样每个人都能很容易地运行它,并且可以使单元测试每天都运行。每个人都可以随时在自己的机器上运行。团队一般是在每日构建中运行单元测试的,这样每个单元测试的错误就能及时被发现并得到修改。

- 请阅读邹欣老师的博客,编写程序的单元测试:[关于单元测试和回归测试](https://www.cnblogs.com/xinz/archive/2011/11/20/2255830.html)
- c++可以使用vs2017。对于博客任务中的单元测试、性能分析(Studio Profiling Tools),vs2017有相应的功能。
- java可以使用idea。也可以自行查找使用其他工具。

<hr/>

# 设计思路

## 使用规则

**单词定义**:以下划线或字母开头的,只包含字母、数字、下划线的字符串称为单词,区分大小写。

**字符定义**:所有字符包括空格,制表符等,即直接使用字符串长度得出。

## 思想过程

1. 最开始想到的解决办法是:通过空格符号计算单词数量,遍历整个字符串,如果遇到空格并且空格前不是空格,则单词数量+1,,遇到换行符则行数+1。

*发现问题:*

问题1:需要进行单词数量统计,要记录每个单词及其数量;

问题2:有些单词不是通过空格分离,不能保证文本字符间的空格数量;

*解决思路*

问题1:用map记录单词及其数量,在遍历的过程中用一个临时变量进行记录;

问题2:用正则表达式将所有非空格非换行符的分隔符号都转化为空格进行判断;

> 突然灵机一闪
>
> 我既然要用正则转化空格那为什么不直接用正则判断??

2. **新的方法**:用正则表达式匹配所有的单词得出单词数和每个单词以及换行符;

> 正则判断:
>
> - 我定义的单词:`/[a-zA-Z_][\w]*/`
> - 换行符:`/\n|\r\n/`
> - 纯粹按空格(包括换行符、制表符、空格符等)划分单词:`/[^\s]/`或者`/[\S]/`
> - 纯粹英文单词:`/[a-zA-Z]+/`

*发现问题:*

问题1:需要一次性读出文件的所有字符,而文件大小是不可以判断的,并且过大量的内容会出现问题;

*解决思路:*

逐行读取文件字符判断,再把每一行的字符数、单词数相加。不必再判断换行符。







































75 changes: 75 additions & 0 deletions 221801227/codeStyle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
- [TOC]

## 缩进

按语言分:基本所有语言都是按Tab键有4个空格;像使用vue时有要求只能2格空格,就会设置tab键的空格数或者直接用空格;

在一个项目中只会有一种缩进方法;

## 区块

类似 for,if 等区块,有些情况可以不用括号。但规范总是使用大括号表示区块。

另外,区块起首的大括号的位置:起首的大括号跟在关键字的后面,不要另起一行。

```
block {
// ...
}
```

## 圆括号

圆括号一种表示函数的调用,另一种表示表达式的组合

```c++
// 圆括号表示函数的调用
tostring();

// 圆括号表示表达式的组合
(1 + 2) * 3
```

用空格,区分这两种不同的括号。

> 1. 表示函数调用时,函数名与左括号之间没有空格。
> 2. 表示函数定义时,函数名与左括号之间没有空格。
> 3. 其他情况时,前面位置的语法元素与左括号之间,都有一个空格。

## 自增和自减运算符

自增(`++`)和自减(`--`)运算符,放在变量的前面或后面,返回的值不一样,很容易发生错误。

在没有影响的情况下,尽量都使用前自增与前自减。

## 变量命名:

驼峰式的命名,比如:myMap,charCnt。尽量要有语义;

## 每行最多字符数

80个字符。如果有不能换行的字符串的情况另看;

## 函数最大行数

30行;

## 函数、类命名

和变量命名一样都是驼峰式,主要看语义;

## 常量

常量用全大写;

## 空行规则

某一个功能所要使用的变量在一起,前后有空行;函数间要有空行;函数内若有for、if等结构,这些结构较长时会空行;

## 注释规则

对代码块的注释单独一行写;对某行的单独解释,若那一行没有很长则直接在后面空一个开始写。注释基本使用快捷键,多行注释也是用单行的符号;

## 操作符前后空格

基本操作符前后都会有空格。如:判断,算数,逻辑等;
87 changes: 87 additions & 0 deletions 221801227/src/head.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#pragma once
#pragma once
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <map>
#include <regex>
using namespace std;

typedef pair<string, int> PAIR;

// ��map��valueֵ��������ת�Ƶ���vector�У�
struct CmpByValue {
bool operator()(const PAIR& lhs, const PAIR& rhs) {
return rhs.second < lhs.second;
}
};

//��map�е�key��������
struct CmpByKeyLength {
bool operator()(const string& k1, const string& k2)const {
return k1.length() < k2.length();
}
};

class fun {
public:
map<string, int, CmpByKeyLength> mymap;
string regexp = "[a-zA-Z]{4}[a-zA-Z0-9]*";

//����һ�У�һ���ַ����У��ĵ�����
int countWord(string str) {
regex words_regex(regexp); // �жϵ��ʵ��������ʽ

auto words_begin = sregex_iterator(
str.begin(),
str.end(),
words_regex);
auto words_end = sregex_iterator();

return distance(words_begin, words_end);
}
// ת��ΪСд
string myToLower(string str) {
for (int i = 0; i < str.length(); ++i) {
str[i] = tolower(str[i]);
}
return str;
}
//��ȡһ���еĵ��ʼ����������map��
void getWord(string str) {
regex words_regex(regexp); // �жϵ��ʵ��������ʽ

auto words_begin = sregex_iterator(
str.begin(),
str.end(),
words_regex);
auto words_end = sregex_iterator();

for (sregex_iterator k = words_begin; k != words_end; ++k) {
smatch match = *k;
string match_str = myToLower(match.str());//��ȡ����
//////////////�����ʷŽ�mapͳ������/////////////////
map<string, int>::iterator iter1;
iter1 = mymap.find(match_str);
if (iter1 == mymap.end()) {
pair<string, int> value(match_str, 1);
mymap.insert(value);
}
else {
++mymap[match_str];
}
}

}

//������ʼ�������
void showWord() {
map<string, int>::iterator strmap_iter2 = mymap.begin();
for (;strmap_iter2 != mymap.end();strmap_iter2++)
{
cout << strmap_iter2->first << ' ' << strmap_iter2->second << endl;
}
}

};
Loading