在 C++ 中读取文件内容并将不同数据类型分离到单独向量中的最佳方法
Best way to read a files contents and separate different data types into separate vectors in C++
我对 C++ 语法相当陌生,想知道是否有人可以提供他们如何解决我遇到的问题。
我的任务是读取包含字符串和整数组合的文件 txt 内容。然后我需要将所有整数存储到一个向量中,将所有字符串存储到另一个向量中。我已经设法将所有内容存储到一个向量中,但现在我想将不同的数据类型分离到它们自己的向量中,但是我正在努力寻找最佳方法。我会遍历整个向量,然后使用数据类型的 if 条件,还是有其他方法?我把我的读取文件代码和文件的例子贴出来,让你更清楚地理解我的意思。
谢谢,
// Basic read file code
fstream file("filepath.txt", ios::in); // reads file
string line; // temp storage of lines in file
vector<string> lines; // creates for permanent storage
while (getline(file, line))
{
lines.push_back(line);
}; // pushes each line to back of vector until file end.
文件示例 - 每个字符串都是一个问题,下面一行是整数形式的答案。共88行。
1, string"
2, int
3,"string"
4, int
5,"string"
6, int
您应该交替创建两个向量和 push_back 数据,希望对您有所帮助:)
你快到了,你的例子中的代码很好。只是缺少第二步:
// storage
std::vector<int> integers;
std::vector<std::string> strings;
// open file and iterate
std::ifstream file( "filepath.txt" );
while ( file ) {
// read one line
std::string line;
std::getline(file, line, '\n');
// create stream for fields
std::istringstream ils( line );
std::string token;
// read integer (I like to parse it and convert separated)
if ( !std::getline(ils, token, ',') ) continue;
int ivalue;
try {
ivalue = std::stoi( token );
} catch (...) {
continue;
}
integers.push_back( ivalue );
// Read string
if ( !std::getline( ils, token, ',' )) continue;
strings.push_back( token );
}
神马:https://godbolt.org/z/4aMv6MW4K
顺便说一句,using std;
实践会在未来对您产生不利影响。代码中尽量保留std::
前缀,比较安全
你在这里问你应该如何解决给定的问题。
在正常的软件开发中,我们会执行几个步骤。首先,我们分析需求,然后想到设计,然后开始编码。最后,我们验证并验证该程序。实际上还有更多的过程。但请采纳一项重要建议:
写代码之前。我们应该先弄清楚“是什么”,然后再弄清楚“怎么做”。
最后但同样重要的是,在进行编码时,我们应该遵循标准模式以避免最常见的问题。
那么,现在让我们看看您的问题。你想读取一个文本文件。文本文件包含以逗号分隔值的行。以逗号分隔值的行应被拆分。
然后,总有 2 行属于一起。第一行包含作为整数的索引和作为字符串的问题,第二行也包含一个整数索引,然后是一个表示答案的整数。
应读取并存储所有数据以供进一步处理。
至此,我们已经大致完成了需求分析。
接下来是“如何”,设计,“我们想如何做事”
您提到您想要使用 2 个不同的向量来存储问题和答案。那种做法基本没那么好
因为一般规则是您应该将以某种方式属于一起的值存储在“struct”或“class”中,即使具有不同的类型,如 int 和 string。首行数据和下一行数据也是如此。
另一方面,许多具有相同类型的数据应该存储在容器中,例如 std::array
或 std::vector
(或其他,取决于用例)。
在您的情况下,您将同时拥有两者。因此,首先是 struct
中不同类型的数据,然后是这些结构的 std::vector
。
上述示例(许多可能的解决方案之一):
#include <iostream>
#include <vector>
struct Question {
int index{};
std::string text{};
};
struct Answer {
int index{};
int data{};
};
struct QuestionAndAnswer {
Question question{};
Answer answer{};
};
std::vector<QuestionAndAnswer> questionAndAnswer{};
好的,接下来就明白了。
我们要打开一个文件并逐行读取它。打开文件进行读取,可以通过定义 std::ifstream
然后将文件名传递给其构造函数来完成。这将为您打开文件。最后,当 std::ifstream
类型的变量超出范围时,std::ifstream
的析构函数将自动为您关闭文件。
如果对 C++ 功能有任何疑问,请务必查看 CPP 参考。
作为一般规则,您应该始终检查任何 IO 操作的结果。这可以用 if (ifstreamVaraible)
来完成。如果您查看 IO-Stream 函数的定义,您会发现其中许多 return 再次引用了已调用的 IO-Stream。示例:
// Open the file
std::ifstream sourceFileStream(“test.txt”);
// Check, if it could be opened successfully
if (sourceFileStream) {
// Read the line and get the result
std::string line{};
if (std::getline(sourceFileStream, line)) {
. . .
}
这是如何工作的?如果您查看流函数的文档,那么您会看到它们的 bool
而不是运算符 !
被覆盖,并且 return 流的状态。对于上面的例子 if (sourceFileStream) {
编译器在 if
语句中看到一个流变量,它需要一个布尔表达式。然后它将获取流的 bool
函数并对其求值。
同样适用于 if (std::getline(sourceFileStream, line))
。这将首先执行 getline
- 操作,这将读取该行。然后 getline
return 是对流的引用。然后 if
- 语句再次包含 quasi if (sourceFileStream)
并且将调用 bool 运算符。
使用此机制,您可以(并且应该)检查所有 IO 操作的结果。
如果我们想在循环中读取多行,那么规则是,将 std::getline(sourceFileStream, line)
语句放入 while 语句的条件部分。否则你总是会看很多行。
您会从经验不足的开发人员那里看到类似“while (!sourceFileStream.eof())”或类似的 while (sourceFileStream)
的内容。这被认为是错误的。她在 SO 上有很多陈述,更详细地解释了这一点。
接下来,如果你想学习C++并使用更好的方法,那么你应该使用面向对象的编程。第一步是,将数据和操作这些数据的方法放在一个 class
或 struct
中。对于您的情况,这意味着输入函数应该是结构的一部分。
在 C++ 中,输入是通过提取器运算符完成的 >>
。因此,我们应该向您的结构添加一个提取器运算符。
它的语法是(以答案 struct=:
为例
struct Answer {
int index{};
int data{};
friend std::istream& operator >> (std::istream& is, Answer& a) {
// Read an index, a comma, and the answer
char comma{};
return is >> a.index >> comma >> a.data;
}
};
这是 class
(或 struct
)“答案”的函数,它将流和答案作为输入,并将再次 return 对流的引用.现在,我们可以写:
Answer answer{};
if (!(std::cin >> answer))
std::cerr << “Problem while reading answer\n”;
if
- 语句将执行嵌入式提取操作。编译器将看到一个流、提取器运算符 >>
和一个 Answer 类型的变量。因此,它将调用上述结构中定义的函数。该操作将 return 流的引用和流的 !
运算符,将指示流是否已发布。
可以为输出实现类似的机制,因此我们也可以覆盖插入器运算符>>
。
综上所述,我们可以将上述大问题分解成非常小的单元,以简单易懂的方式实现。
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <iomanip>
struct Question {
// Data
int index{};
std::string text{};
// Extractor operator
friend std::istream& operator >> (std::istream& is, Question& q) {
char comma{};
// First read the index, then the comma, then eat up white space. A reference to is will be returned. getline
// will read the rest of the line by using is. It will also return is, which is then the function return value
return std::getline(is >> q.index >> comma >> std::ws, q.text);
}
// Simple output
friend std::ostream& operator << (std::ostream& os, const Question& q) {
return os << "Question: Index: " << q.index << "\tText: " << q.text;
}
};
struct Answer {
// Data
int index{};
int data{};
// Extractor operator
friend std::istream& operator >> (std::istream& is, Answer& a) {
char comma{};
// Read the index, then the comma, then data. A reference to is will be returned.
return is >> a.index >> comma >> a.data;
}
// Simple output
friend std::ostream& operator << (std::ostream& os, const Answer& a) {
return os << "Answer: Index: " << a.index << "\tData: " << a.data;
}
};
struct QuestionAndAnswer {
// Data
Question question{};
Answer answer{};
// Extractor operator
friend std::istream& operator >> (std::istream& is, QuestionAndAnswer& q) {
// Read question and answer
return is >> q.question >> q.answer;
}
// Simple output
friend std::ostream& operator << (std::ostream& os, const QuestionAndAnswer& q) {
return os << q.question << "\t\t" << q.answer;
}
};
int main() {
// Here we will store all questions and answers
std::vector<QuestionAndAnswer> questionAndAnswer{};
// Open the source file and check, if it could be opened
std::ifstream sourceFileStream("r:\filepath.txt");
if (sourceFileStream) {
QuestionAndAnswer temp{};
// Read all questions and answers in a loop
while (sourceFileStream >> temp)
questionAndAnswer.push_back(temp);
// Show debug output
for (const QuestionAndAnswer& qa : questionAndAnswer)
std::cout << qa << '\n';
}
else
std::cerr << "\nError: Could not open source file\n";
}
使用这种方法是为 C++ 程序员推荐的众多方法之一。
一个源文件包含
1, Question 1
2, 1
3, Question 2
4, 2
5, Question 3
6, 3
7, Question 4
8, 4
9, Question 5
10, 5
将创建输出:
Question: Index: 1 Text: Question 1 Answer: Index: 2 Data: 1
Question: Index: 3 Text: Question 2 Answer: Index: 4 Data: 2
Question: Index: 5 Text: Question 3 Answer: Index: 6 Data: 3
Question: Index: 7 Text: Question 4 Answer: Index: 8 Data: 4
Question: Index: 9 Text: Question 5 Answer: Index: 10 Data: 5
如果你想坚持你最初的想法,那么你可以使用:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <iomanip>
int main() {
// Here we will store all questions and answers
std::vector<std::string> questions{};
std::vector<int> answers{};
// Open the source file and check, if it could be opened
std::ifstream sourceFileStream("r:\filepath.txt");
if (sourceFileStream) {
std::string question{};
int temp{}, answer{};
char comma{};
// Read all questions and answers in a loop
while (std::getline(sourceFileStream >> temp >> comma >> std::ws, question))
if (sourceFileStream >> temp >> comma >> answer) {
// We do not want to go out fo sync. Always store questions and answers together
questions.push_back(question);
answers.push_back(answer);
}
// Debug output
for (unsigned int k = 0; k < questions.size(); ++k)
std::cout << "Question: " << questions[k] << "\t\tAnswer: " << answers[k] << '\n';
}
else
std::cerr << "\nError: Could not open source file\n";
}
但可能不那么推荐...
我对 C++ 语法相当陌生,想知道是否有人可以提供他们如何解决我遇到的问题。
我的任务是读取包含字符串和整数组合的文件 txt 内容。然后我需要将所有整数存储到一个向量中,将所有字符串存储到另一个向量中。我已经设法将所有内容存储到一个向量中,但现在我想将不同的数据类型分离到它们自己的向量中,但是我正在努力寻找最佳方法。我会遍历整个向量,然后使用数据类型的 if 条件,还是有其他方法?我把我的读取文件代码和文件的例子贴出来,让你更清楚地理解我的意思。
谢谢,
// Basic read file code
fstream file("filepath.txt", ios::in); // reads file
string line; // temp storage of lines in file
vector<string> lines; // creates for permanent storage
while (getline(file, line))
{
lines.push_back(line);
}; // pushes each line to back of vector until file end.
文件示例 - 每个字符串都是一个问题,下面一行是整数形式的答案。共88行。
1, string"
2, int
3,"string"
4, int
5,"string"
6, int
您应该交替创建两个向量和 push_back 数据,希望对您有所帮助:)
你快到了,你的例子中的代码很好。只是缺少第二步:
// storage
std::vector<int> integers;
std::vector<std::string> strings;
// open file and iterate
std::ifstream file( "filepath.txt" );
while ( file ) {
// read one line
std::string line;
std::getline(file, line, '\n');
// create stream for fields
std::istringstream ils( line );
std::string token;
// read integer (I like to parse it and convert separated)
if ( !std::getline(ils, token, ',') ) continue;
int ivalue;
try {
ivalue = std::stoi( token );
} catch (...) {
continue;
}
integers.push_back( ivalue );
// Read string
if ( !std::getline( ils, token, ',' )) continue;
strings.push_back( token );
}
神马:https://godbolt.org/z/4aMv6MW4K
顺便说一句,using std;
实践会在未来对您产生不利影响。代码中尽量保留std::
前缀,比较安全
你在这里问你应该如何解决给定的问题。
在正常的软件开发中,我们会执行几个步骤。首先,我们分析需求,然后想到设计,然后开始编码。最后,我们验证并验证该程序。实际上还有更多的过程。但请采纳一项重要建议:
写代码之前。我们应该先弄清楚“是什么”,然后再弄清楚“怎么做”。
最后但同样重要的是,在进行编码时,我们应该遵循标准模式以避免最常见的问题。
那么,现在让我们看看您的问题。你想读取一个文本文件。文本文件包含以逗号分隔值的行。以逗号分隔值的行应被拆分。
然后,总有 2 行属于一起。第一行包含作为整数的索引和作为字符串的问题,第二行也包含一个整数索引,然后是一个表示答案的整数。
应读取并存储所有数据以供进一步处理。
至此,我们已经大致完成了需求分析。
接下来是“如何”,设计,“我们想如何做事”
您提到您想要使用 2 个不同的向量来存储问题和答案。那种做法基本没那么好
因为一般规则是您应该将以某种方式属于一起的值存储在“struct”或“class”中,即使具有不同的类型,如 int 和 string。首行数据和下一行数据也是如此。
另一方面,许多具有相同类型的数据应该存储在容器中,例如 std::array
或 std::vector
(或其他,取决于用例)。
在您的情况下,您将同时拥有两者。因此,首先是 struct
中不同类型的数据,然后是这些结构的 std::vector
。
上述示例(许多可能的解决方案之一):
#include <iostream>
#include <vector>
struct Question {
int index{};
std::string text{};
};
struct Answer {
int index{};
int data{};
};
struct QuestionAndAnswer {
Question question{};
Answer answer{};
};
std::vector<QuestionAndAnswer> questionAndAnswer{};
好的,接下来就明白了。
我们要打开一个文件并逐行读取它。打开文件进行读取,可以通过定义 std::ifstream
然后将文件名传递给其构造函数来完成。这将为您打开文件。最后,当 std::ifstream
类型的变量超出范围时,std::ifstream
的析构函数将自动为您关闭文件。
如果对 C++ 功能有任何疑问,请务必查看 CPP 参考。
作为一般规则,您应该始终检查任何 IO 操作的结果。这可以用 if (ifstreamVaraible)
来完成。如果您查看 IO-Stream 函数的定义,您会发现其中许多 return 再次引用了已调用的 IO-Stream。示例:
// Open the file
std::ifstream sourceFileStream(“test.txt”);
// Check, if it could be opened successfully
if (sourceFileStream) {
// Read the line and get the result
std::string line{};
if (std::getline(sourceFileStream, line)) {
. . .
}
这是如何工作的?如果您查看流函数的文档,那么您会看到它们的 bool
而不是运算符 !
被覆盖,并且 return 流的状态。对于上面的例子 if (sourceFileStream) {
编译器在 if
语句中看到一个流变量,它需要一个布尔表达式。然后它将获取流的 bool
函数并对其求值。
同样适用于 if (std::getline(sourceFileStream, line))
。这将首先执行 getline
- 操作,这将读取该行。然后 getline
return 是对流的引用。然后 if
- 语句再次包含 quasi if (sourceFileStream)
并且将调用 bool 运算符。
使用此机制,您可以(并且应该)检查所有 IO 操作的结果。
如果我们想在循环中读取多行,那么规则是,将 std::getline(sourceFileStream, line)
语句放入 while 语句的条件部分。否则你总是会看很多行。
您会从经验不足的开发人员那里看到类似“while (!sourceFileStream.eof())”或类似的 while (sourceFileStream)
的内容。这被认为是错误的。她在 SO 上有很多陈述,更详细地解释了这一点。
接下来,如果你想学习C++并使用更好的方法,那么你应该使用面向对象的编程。第一步是,将数据和操作这些数据的方法放在一个 class
或 struct
中。对于您的情况,这意味着输入函数应该是结构的一部分。
在 C++ 中,输入是通过提取器运算符完成的 >>
。因此,我们应该向您的结构添加一个提取器运算符。
它的语法是(以答案 struct=:
为例struct Answer {
int index{};
int data{};
friend std::istream& operator >> (std::istream& is, Answer& a) {
// Read an index, a comma, and the answer
char comma{};
return is >> a.index >> comma >> a.data;
}
};
这是 class
(或 struct
)“答案”的函数,它将流和答案作为输入,并将再次 return 对流的引用.现在,我们可以写:
Answer answer{};
if (!(std::cin >> answer))
std::cerr << “Problem while reading answer\n”;
if
- 语句将执行嵌入式提取操作。编译器将看到一个流、提取器运算符 >>
和一个 Answer 类型的变量。因此,它将调用上述结构中定义的函数。该操作将 return 流的引用和流的 !
运算符,将指示流是否已发布。
可以为输出实现类似的机制,因此我们也可以覆盖插入器运算符>>
。
综上所述,我们可以将上述大问题分解成非常小的单元,以简单易懂的方式实现。
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <iomanip>
struct Question {
// Data
int index{};
std::string text{};
// Extractor operator
friend std::istream& operator >> (std::istream& is, Question& q) {
char comma{};
// First read the index, then the comma, then eat up white space. A reference to is will be returned. getline
// will read the rest of the line by using is. It will also return is, which is then the function return value
return std::getline(is >> q.index >> comma >> std::ws, q.text);
}
// Simple output
friend std::ostream& operator << (std::ostream& os, const Question& q) {
return os << "Question: Index: " << q.index << "\tText: " << q.text;
}
};
struct Answer {
// Data
int index{};
int data{};
// Extractor operator
friend std::istream& operator >> (std::istream& is, Answer& a) {
char comma{};
// Read the index, then the comma, then data. A reference to is will be returned.
return is >> a.index >> comma >> a.data;
}
// Simple output
friend std::ostream& operator << (std::ostream& os, const Answer& a) {
return os << "Answer: Index: " << a.index << "\tData: " << a.data;
}
};
struct QuestionAndAnswer {
// Data
Question question{};
Answer answer{};
// Extractor operator
friend std::istream& operator >> (std::istream& is, QuestionAndAnswer& q) {
// Read question and answer
return is >> q.question >> q.answer;
}
// Simple output
friend std::ostream& operator << (std::ostream& os, const QuestionAndAnswer& q) {
return os << q.question << "\t\t" << q.answer;
}
};
int main() {
// Here we will store all questions and answers
std::vector<QuestionAndAnswer> questionAndAnswer{};
// Open the source file and check, if it could be opened
std::ifstream sourceFileStream("r:\filepath.txt");
if (sourceFileStream) {
QuestionAndAnswer temp{};
// Read all questions and answers in a loop
while (sourceFileStream >> temp)
questionAndAnswer.push_back(temp);
// Show debug output
for (const QuestionAndAnswer& qa : questionAndAnswer)
std::cout << qa << '\n';
}
else
std::cerr << "\nError: Could not open source file\n";
}
使用这种方法是为 C++ 程序员推荐的众多方法之一。
一个源文件包含
1, Question 1
2, 1
3, Question 2
4, 2
5, Question 3
6, 3
7, Question 4
8, 4
9, Question 5
10, 5
将创建输出:
Question: Index: 1 Text: Question 1 Answer: Index: 2 Data: 1
Question: Index: 3 Text: Question 2 Answer: Index: 4 Data: 2
Question: Index: 5 Text: Question 3 Answer: Index: 6 Data: 3
Question: Index: 7 Text: Question 4 Answer: Index: 8 Data: 4
Question: Index: 9 Text: Question 5 Answer: Index: 10 Data: 5
如果你想坚持你最初的想法,那么你可以使用:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <iomanip>
int main() {
// Here we will store all questions and answers
std::vector<std::string> questions{};
std::vector<int> answers{};
// Open the source file and check, if it could be opened
std::ifstream sourceFileStream("r:\filepath.txt");
if (sourceFileStream) {
std::string question{};
int temp{}, answer{};
char comma{};
// Read all questions and answers in a loop
while (std::getline(sourceFileStream >> temp >> comma >> std::ws, question))
if (sourceFileStream >> temp >> comma >> answer) {
// We do not want to go out fo sync. Always store questions and answers together
questions.push_back(question);
answers.push_back(answer);
}
// Debug output
for (unsigned int k = 0; k < questions.size(); ++k)
std::cout << "Question: " << questions[k] << "\t\tAnswer: " << answers[k] << '\n';
}
else
std::cerr << "\nError: Could not open source file\n";
}
但可能不那么推荐...