这是我编程生涯的一块里程碑,作为菜鸟小白,一直在底层仰望程序天空中自由翱翔的前辈们,这次自己起飞了一下下,认识到了数据结构的巨大魅力和无限潜力,感受到了编程带来的快乐!
这里简单记录,以备后续使用。
问题描述
有一份文件,示例如下,前6个字符表示编号,编号应从0-8000左右,但由于某种原因(不重要啦),生成的顺序被打乱,但序号和该行的内容是匹配的。
007453,-1,621,754,1109,927,0.9999844,-1,-1,-1
007453,-1,646,330,1095,522,0.99999344,-1,-1,-1
005556,-1,588,681,1004,870,0.9999943,-1,-1,-1
005556,-1,891,166,1222,504,0.99999654,-1,-1,-1
007181,-1,1033,130,1259,513,0.99998987,-1,-1,-1
007181,-1,628,593,1056,846,0.9999931,-1,-1,-1
……………………
现在需要按照编号递增的顺序,将每行重新排列。排好后应该是这样的:
000001,-1,607,306,947,518,0.999977,-1,-1,-1
000001,-1,597,700,938,922,0.99997723,-1,-1,-1
000002,-1,608,309,955,519,0.9999672,-1,-1,-1
000002,-1,601,698,919,918,0.99997675,-1,-1,-1
000003,-1,599,302,944,519,0.99996614,-1,-1,-1
000003,-1,603,698,936,920,0.9999789,-1,-1,-1
……………………
待解决的难点:
1、文件读取时,仅使用string读取,句首出现乱码;(使用宽字符串wstring解决)
2、读取文件后,如何减少循环查询的次数。
方法一:暴力解决法
方法一是循环8000次(大约),每次循环过程中,制作该次循环要查找的标准格式号码,每读取到一行非空的内容,取出该行的前6个字符,与制作的号码比较,如果相等说明找到了目标行,把该行和该行下面的行(直到遇到空行)一起写入输出文件。
下面这个程序跑了将近一个小时才出结果:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <io.h>
#include <codecvt>//读取utf-8文件
using namespace std;
int main()
{
string fileName = "result.txt";//文件名称
ofstream out("allout.txt", ofstream::app);//以追加形式写入
if (!out) {
cerr << "无法打开输出文件" << endl;
return -1;
}
int j=0;
wstring_convert<std::codecvt_utf8<wchar_t>> conv;
for (int i = 1;i < 8770;++i) {
//制作应有的名称
string numstr,nullstring="";
if (i < 10) numstr = nullstring+"00000" + to_string(i);
else if(i<100) numstr = nullstring + "0000" + to_string(i);
else if (i < 1000) numstr = nullstring + "000" + to_string(i);
else if (i < 10000) numstr = nullstring + "00" + to_string(i);
wstring wnumstr = conv.from_bytes(numstr);
string str;
ifstream in(fileName);
if (in)//若文件打开成功
{
while (getline(in, str))//逐行获取in句柄绑定的文件内容
{
wstring wstr = conv.from_bytes(str);
if (!str.empty() && str != " ") {
wstring wstr1(wstr, 0, 6);//获取每行前6个字符
if (wnumstr == wstr1) {//找到了首个
out << str << endl;
getline(in, str);
out << str << endl;
getline(in, str);
if(!str.empty() && str != " ")
out << str << endl;
break;
}
}
}
}
else {//若文件打开失败
cerr << "无法打开输入文件" << endl;
return -1;
}
}
return 0;
}
方法二:巧用multimap
在multimap数据结构的帮助下,一秒出结果!!!
方法二使用multimap<string,string>,第一个string存储非空行的前6个字符(即号码),第二个string存储该行的整行内容。
由于multimap具备自动根据第一个string的字典序排序的能力,所以整个过程只需要一趟循环,便可以将文件中所有的内容存放到multimap中,大约占用800Kb,还是可以接受的, 相当于牺牲空间换时间了。
multimap最具吸引力的就是:只要把数据存好得到的就是排好序的,这是在太高效了!最后再来一趟遍历,输出保存的内容,全程高速!
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <io.h>
#include <codecvt>//读取utf-8文件
#include <map>
using namespace std;
int main()
{
string fileName = "result.txt";//文件名称
ofstream out("allout.txt", ofstream::app);//以追加形式写入
if (!out) {
cerr << "无法打开输出文件" << endl;
return -1;
}
wstring_convert<std::codecvt_utf8<wchar_t>> conv;//用于wstring和string的转换
multimap<string, string> mymultimap;//应取得C位!!!
string str;
ifstream in(fileName);
if (in)//若文件打开成功
{
while (getline(in, str))//逐行获取in句柄绑定的文件内容
{
wstring wstr = conv.from_bytes(str);
if (!str.empty() && str != " ") {
wstring wstr1(wstr, 0, 6);//获取每行前6个字符
string str1 = conv.to_bytes(wstr1);
auto ret = mymultimap.insert({ str1,str });//将<前缀,该行内容>存入map
}
}
//遍历multimap,将内容输出到文件
for (auto i : mymultimap) {
out << i.second << endl;
}
}
else {//若文件打开失败
cerr << "无法打开输入文件" << endl;
return -1;
}
return 0;
}