在 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::arraystd::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++并使用更好的方法,那么你应该使用面向对象的编程。第一步是,将数据和操作这些数据的方法放在一个 classstruct 中。对于您的情况,这意味着输入函数应该是结构的一部分。

在 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";
}

但可能不那么推荐...