问题描述
输入文件:在每一行中,有一个条目是一对ID - name - GPA
。值以制表符分隔。
20210001 Bill 3.61
20210002 Joe 3.21
20210003 Royce 4.32
20210004 Lucy 2.21
我必须重新排列按 GPA 排序的文件(按降序排列)。
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
int main() {
ifstream inputfile("input.txt");
ofstream outputfile("output.txt");
if (inputfile.fail()) {
cout << "Cannot open inputfile" << endl;
}
if (outputfile.fail()) {
cout << "Cannot open outputfile" << endl;
}
if (inputfile.is_open()) {
string line;
while (getline(inputfile,line)) {
string token;
stringstream ss(line);
while (getline(ss,token,'\t')) {
}
}
}
inputfile.close();
outputfile.close();
return 0;
}
我不知道接下来要做什么。
解决方法
在从/到流(如文件流)进行 I/O 操作时,通常更容易创建一个类来保存文件中每条记录的数据,并为 operator>>
(in) 和operator<<
(出)。
示例:
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
// one line in the file could possibly have this representation in your program:
struct record {
std::uint32_t ID;
std::string name;
double GPA;
};
// an overload to read one line of data from an istream (like an ifstream) using getline
std::istream& operator>>(std::istream& is,record& r) {
if(std::string line; std::getline(is,line)) {
std::istringstream iss(line);
if(not (iss >> r.ID >> r.name>> r.GPA)) {
is.setstate(std::ios::failbit);
}
}
return is;
}
// an overload to write one line to an ostream (like an ofstream)
std::ostream& operator<<(std::ostream& os,const record& r) {
return os << r.ID<< '\t' << r.name << '\t' << r.GPA << '\n';
}
有了那个样板,制作实际程序就变得容易了。
int main() {
std::ifstream inputfile("input.txt");
// read all records from the file into a vector
std::vector<record> records(
std::istream_iterator<record>(inputfile),std::istream_iterator<record>{}
);
// sort the records according to GPA
// if you want a decending order,just make it return rhs.GPA < lhs.GPA;
std::sort(records.begin(),records.end(),[](const record& lhs,const record& rhs) {
return lhs.GPA < rhs.GPA;
}
);
std::ofstream outputfile("output.txt");
// put the result in the output file
std::copy(records.begin(),std::ostream_iterator<record>(outputfile));
}
这个答案中可能有一些你以前没见过的东西。我将列出我认为可能需要阅读的资源:
-
operator<<
/operator>>
重载。见Stream extraction and insertion -
std::istringstream
- 你在里面放了一个std::string
- 然后它的行为就像任何其他std::istream
(就像一个std::ifstream
)。 -
std::istream_iterator
/std::ostream_iterator
-
std::sort
/std::copy
是标准库中众多 algorithms 中的两个。 -
std::vector
- 用作动态数组的类模板,您可以在其中添加和删除数据,例如本答案中的record
。 -
Lambda expressions - 在这个答案中,它是用于对记录进行排序的函子
[](const record& lhs,const record& rhs) { ... }
。
如果您确切知道一行中有多少个标记:
您只需使用制表符分隔符 getline()
3 次,然后分别存储这些值。
string id;
string name;
string gpa;
getline(ss,id,'\t');
getline(ss,name,gpa,'\t');
此逻辑将存在于您的循环中,循环遍历文件中的行。
,您可以使用 struct
来包含您的所有字段:
struct user
{
int id; string name; double point;
};
然后将它们全部插入到 std::vector
中,最后使用 sort()
和 comp
参数按点排序。
代码:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;
struct user
{
int id; string name; double point;
};
vector<user> users;
void stripInfoFromString(string inp)
{
stringstream cur(inp);
user curUser;
cur >> curUser.id >> curUser.name >> curUser.point;
users.push_back(curUser);
}
bool compareUser(user x,user y)
{
return x.point < y.point;
}
int main()
{
string a1 = "20210001 Bill 3.61";
string a2 = "20210002 Joe 3.21";
string a3 = "20210003 Royce 4.32";
string a4 = "20210004 Lucy 2.21";
stripInfoFromString(a1);
stripInfoFromString(a2);
stripInfoFromString(a3);
stripInfoFromString(a4);
sort(users.begin(),users.end(),compareUser);
cout << fixed << setprecision(2);
for (user cur : users)
{
cout << cur.id << " " << cur.name << " " << cur.point << "\n";
}
}
输出:
20210004 Lucy 2.21
20210002 Joe 3.21
20210001 Bill 3.61
20210003 Royce 4.32
- 我使用标准输入/输出来最小化代码,您可以轻松切换文件输入。
更多信息:
struct
: https://en.cppreference.com/w/c/language/struct
sort()
: https://en.cppreference.com/w/cpp/algorithm/sort
另外,请参阅此处why is using namespace std;
considered bad practice。
我建议定义一个 struct
来保存 3 个令牌,然后为每一行创建一个 std::vector
保存该 struct
的实例。然后,您可以在第三个标记上对该 vector
进行排序。您的标头包含中已包含 <vector>
。
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
struct entry
{
int id;
string name;
double gpa;
};
int main() {
ifstream inputfile("input.txt");
ofstream outputfile("output.txt");
vector<entry> entries;
if (inputfile.fail()) {
cout << "Cannot open inputfile" << endl;
}
if (outputfile.fail()) {
cout << "Cannot open outputfile" << endl;
}
string line;
while (getline(inputfile,line)) {
istringstream iss(line);
entry e;
string token;
getline(iss,token,'\t');
e.id = stoi(token);
getline(iss,e.name,'\t');
getline(iss,'\t');
e.gpa = stod(token);
/* alternatively:
iss >> e.id >> e.name >> e.gpa;
*/
entries.push_back(e);
}
inputfile.close();
outputfile.close();
sort(entries.begin(),entries.end(),[](const entry &e1,const entry &e2){
return e1.gpa > e2.gpa;
}
);
for (const entry &e : entries) {
outputfile << e.id << '\t' << e.name << '\t' << e.gpa << '\n';
}
return 0;
}