上个月,帮老师处理了第四轮学科评估的统计结果,其中运用了C++刚学习的文件操作知识,简化了工作量。
详情可见我的另一个博客C++读取文件的实际应用
本来以为事情告一段落,后来老师对数据有提出了新的要求(汗ヽ(ー_ー)ノ)
具体要求如下:
这是原来的Excel显示的不同大学的B以上的学科信息:
老师之前说排成这样,没想到学院要求这个数据要录入数据库,必须更改excel格式:
需要改成这个样子:
这个处理过程可以大致分为两步,例如我们要处理北京大学:
1. 需要先数出北大里B以上有多少个学科(25个),然后在excel中插入这么多行,然后把原来一个单元格的内容根据逗号分行,在一个个粘贴到分好的行中。
2. 根据学科信息与学校信息,查询这个学科的评估分是哪个等级
这个过程看似简单,实际上操作起来相当繁琐!
第一步,首先数学校有多少个学科,按照个数插入行,然后分行,复制,转置粘贴。以北京大学为例,操作一次需要3分钟左右,需要这样处理的大学有近150所,这样粗略估计需要6-7小时的重复操作(中间还不能出错)。
第二步,这一步是查找这个学科的排名,更为麻烦,以查找北京大学物理学的学科评估等级,首先在另一个excel中找到物理学,然后在这一行中查找北京大学,然后发现是在A+列,然后在回到excel输入A+,操作一次大约30s左右,需要统计的学科大约有1500个,大约需要这样的重复操作12个小时!
果断放弃手动处理(也不可能手动处理(T_T))
因此对于这个问题的两个步骤,我采用了两个方法分别解决
Excel与Word中的宏
关于什么是宏,以及宏该如何使用,如果以后有机会,会专门写一个分类,大家只要知道,在Excel与Word等office软件中,可以打开开发工具,使用宏,最简单的就是录制,更复杂一点的需要自己更改VBA中的代码,这是处理Office软件中的重复性的工作的一个极其高效工具
录制完宏以后设置一个快捷键,即可快速调用宏的操作。
效果如下:
从图片我们可以看到,宏根据不同学校里学科个数进行自动的添加行,左边合并单元格操作
然后在使用一个宏,将原来的学科内容进行分行,转置粘贴操作:
效果如下:
这样我们就完成了问题的第一步!(两个操作加起来不超过5分钟,但是初学宏的同学可能需要了解一下VBA代码的知识)
C++文件的读写
使用过宏以后,工作就完成了一半:
接下来就是要对每个学科进行查找。
在这里我使用了C++的文件处理操作
具体分为一下几个步骤:
1. 构建一个用于存储学科信息的类
class subject
{
private:
string subjectname;//学科名称
vector<string> Rank[5];//该学科排名B以上的学校
public:
subject();
subject(string);
void show();
string find(string);
};
这是构造的储存学科信息的类,每一个subject对象包含这个学科的名称以及这个学科排名B以上的学校名,其中
Rank[0]到Rank[4]分别对应A+,A,A-,B+,B的学校
定义的关键的函数有两个:
- 构造函数
通过参数string filename对subject类进行构造,是通过读取filename的文件对subject对象进行初始化,这其中涉及了文件的操作
subject::subject(string filename)
{
ifstream file;
file.open(filename);
if (!file)
{
cout << "读取数据失败";
}
else
{
string temp;
file >> subjectname;//文件的第一行是学科名称,之后是每个等级的学校名字
int i = -1;
while (file >> temp)
{
if (temp == "V")
{
i++; continue;
}
if (temp == "END")
{
break;
}
Rank[i].push_back(temp);
}
}
}
文件格式是这样的(以数学为例)
每一个等级都是通过字符V进行划分的
- 查找函数
查找函数根据传入的大学的名字查找这个大学该学科的排名是属于哪一类
string subject::find(string uni)
{
string rank[] = { "A+", "A", "B+", "B", "B-" };
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < Rank[i].size(); j++)
{
if (uni == Rank[i][j]) return rank[i];
}
}
return "NOT Find";
}
在主函数中,使用一个循环将57个学科的信息储存:
subject *S[57];
for (int i = 0; i < 57; i++)
{
S[i]=new subject(getfilename(i+1));
}
//其中getfilename函数是用于获取读取文件名的函数
2. 构建一个用于存储学校信息的结构体
通过构造subject对象,我们将要存储的学科信息已经存好了,接下来就是读取要查找的学校信息,每一个学校都有很多学科要查,因此定义一个学校的结构体:
struct school
//结构体包含学校信息,以及学校后要查找的学科的名字的字符串组
{
string name;
vector<string> sujectname;
};
定义好结构体,我们需要将要查的所有学校存储在一个结构体数组中,因此定义了一个getdata函数对结构体数组进行文件读取操作:
void getdata(vector<school> &uni)
{
ifstream in("待查信息.txt");
if (!in)
{
cout << "打开待查信息失败" << endl;
return;
}
string temp;
school tep;
int i = 1;
while (in >> temp)
{
if (temp.find("大学") != string::npos || temp.find("学院") != string::npos)
{
if (i == 1)
{
tep.name = temp;
i++;
}
else
{
uni.push_back(tep);
tep.name = temp;
tep.sujectname.clear();
i++;
}
continue;
}
else
{
tep.sujectname.push_back(temp);
}
}
in.close();
}
其中待查信息的txt格式是这样的:
3. 查找匹配每个学校的学科分
存好学科信息与学校信息,接下来就是要互相匹配,将结果输出到一个文件中
ofstream out("out.txt");
for (int i = 0; i < uni.size(); i++)
{
out << uni[i].name<<endl;
for (int j = 0; j < uni[i].sujectname.size(); j++)
{
int key = findnumber(uni[i].sujectname[j]);
out << S[key]->find(uni[i].name) << endl;
}
}
cout << "查找完毕!" << endl;
这其中第七行使用了subject类中的find函数
最后out.txt的结果如下
之后将结果直接复制到Excel中即可。
C++的源程序代码上传到github网站上,链接如下:
至此终于完成了老师布置的工作(T ^ T)