gpt4 book ai didi

C++:读取文本文件中不确定数量的变量并在struct中修改,然后替换文本文件中的行

转载 作者:行者123 更新时间:2023-11-28 04:06:51 26 4
gpt4 key购买 nike

我们的讲师要求我们读取一个文本文件并将它们存储到不同的结构和数组中,然后允许用户修改。但是当我使用 fstream >> name >> day >>...>> roster并逐行获取文本。它总是出错,可能是因为每行的大小不同。

类头

Class Course
private:
string name;
int num_of_student;
string *roster;

Struct class_time
string day;
string time;

test.txt(第一行可以忽略)
<name><day><time><num_of_student><roster(student ids separate by space)>
CS T 2pm 3 01 02 03
Math TH 10am 2 03 04

我不知道如何读取这种类型的文本文件并将它们存储到不同的数组或局部变量中。

更新:
这就是我在成功获取文件中的每个变量之后所做的事情。
num_courses(int){
// function get num of lines in text file
}

void load_Data(){
fstream read;
string name1, day1, time1;
int enroll;
int num_c; // number of courses
read.open("test.txt",ios::in);
if(!read.is_open()){
cout << "No file exist." << endl;
}
num_courses(num_c);
Course course[num_c];
class_time sch[num_c];
while(!read.eof()) {
for (int i = 0; i < num_c; i++) {
read >> name1 >> day1 >> time1 >> enroll;
course[i].name = name1;
sch[i].day = day1;
sch[i].time = time1;
course[i].num_of_student = enroll;
string ros[enroll];
for (int j = 0; j < enroll; j++) {
read >> ros[j];
course[i].roster[j] = ros[j];
}
}
}

现在我想在修改这些变量后替换 test.txt 中的某一行,我应该使用 ofstream并删除文件中的所有内容,并替换为更新的类(class)信息。或者有一种简单的方法可以替换文本文件中的某一行?

最佳答案

好像你想读取 csv 数据。但是,您需要忽略标题行-

我建议使用“现代”C++ 方法。

仍然所有谈论 csv 的人都链接到 How can I read and parse CSV files in C++? ,问题是从 2009 年开始,现在已经 10 多年了。大多数答案也很旧且非常复杂。所以,也许是时候改变了。

在现代 C++ 中,您有迭代范围的算法。你会经常看到类似“someAlgoritm(container.begin(), container.end(), someLambda)”的东西。这个想法是我们迭代一些相似的元素。

在您的情况下,我们遍历输入字符串中的标记,并创建子字符串。这称为标记化。

正是为了这个目的,我们有 std::sregex_token_iterator .而且因为我们有为此目的而定义的东西,我们应该使用它。

这东西是一个迭代器。用于迭代字符串,因此是正则表达式。开始部分定义了我们将操作的输入范围,然后有一个 std::regex对于输入字符串中应该匹配/不应该匹配的内容。匹配策略的类型由最后一个参数给出。

  • 1 --> 给我在正则表达式和
  • 中定义的东西
  • -1 --> 告诉我根据正则表达式不匹配的内容。

  • 所以,既然我们理解了迭代器,我们就可以 std::copy 迭代器中的记号到我们的目标,即 std::vector。的 std::string .由于我们不知道我们有多少列,我们将使用 std::back_inserter作为目标。这将添加我们从 std::sregex_token_iterator 获得的所有 token 。并将其附加到我们的 std::vector<std::string>> .我们有多少列并不重要。

    好的。这样的声明可能看起来像
    std::copy(                          // We want to copy something
    std::sregex_token_iterator // The iterator begin, the sregex_token_iterator. Give back first token
    (
    line.begin(), // Evaluate the input string from the beginning
    line.end(), // to the end
    re, // Add match a comma
    -1 // But give me back not the comma but everything else
    ),
    std::sregex_token_iterator(), // iterator end for sregex_token_iterator, last token + 1
    std::back_inserter(cp.columns) // Append everything to the target container
    );

    现在我们可以理解,这个复制操作是如何工作的。

    下一步。我们想从文件中读取。该文件还包含某种相同的数据。相同的数据是行。

    如上所述,我们可以迭代相似的数据。如果是文件输入或其他。为此,C++ 有 std::istream_iterator .这是一个模板,作为模板参数,它获取应该读取的数据类型,作为构造函数参数,它获取对输入流的引用。没关系,如果输入流是 std::cin , 或 std::ifstreamstd::istringstream .所有类型的流的行为都是相同的。

    由于我们没有 SO 文件,因此我使用(在下面的示例中) std::istringstream存储输入的 csv 文件。当然你可以打开一个文件,通过定义 std::ifstream testCsv(filename) .没问题。

    std::istream_iterator ,我们遍历输入并读取相似的数据。在我们的例子中,一个问题是我们想要迭代特殊数据而不是一些内置数据类型。

    为了解决这个问题,我们定义了一个代理类,它为我们做内部工作(我们不想知道如何,应该封装在代理中)。在代理中,我们覆盖类型转换运算符,以将结果变为我们预期的 std::istream_iterator 类型。 .

    最后一个重要的步骤。一个 std::vector有一个范围构造函数。它还有很多其他构造函数,我们可以在 std::vector 类型变量的定义中使用它们。 .但是对于我们的目的,这个构造函数最适合。

    所以我们定义了一个变量 csv 并使用它的范围构造函数并给它一个范围的开始和一个范围的结束。而且,在我们的具体示例中,我们使用 std::istream_iterator 的开始和结束迭代器。 .

    如果我们结合以上所有内容,读取完整的 CSV 文件是单行的,它是定义一个变量并调用其构造函数。

    请查看生成的代码:
    #include <iostream>
    #include <sstream>
    #include <fstream>
    #include <string>
    #include <vector>
    #include <iterator>
    #include <regex>
    #include <algorithm>

    std::istringstream testCsv{ R"(CS T 2pm 3 01 02 03
    Math TH 10am 2 03 04
    )" };


    // Define Alias for Easier Reading
    using Columns = std::vector<std::string>;
    using CSV = std::vector<Columns>;


    // Proxy for the input Iterator
    struct ColumnProxy {
    // Overload extractor. Read a complete line
    friend std::istream& operator>>(std::istream& is, ColumnProxy& cp) {

    // Read a line
    std::string line; cp.columns.clear();
    std::getline(is, line);

    // The delimiter
    const std::regex re(" ");

    // Split values and copy into resulting vector
    std::copy(std::sregex_token_iterator(line.begin(), line.end(), re, -1),
    std::sregex_token_iterator(),
    std::back_inserter(cp.columns));
    return is;
    }

    // Type cast operator overload. Cast the type 'Columns' to std::vector<std::string>
    operator std::vector<std::string>() const { return columns; }
    protected:
    // Temporary to hold the read vector
    Columns columns{};
    };


    int main()
    {
    // Define variable CSV with its range constructor. Read complete CSV in this statement, So, one liner
    CSV csv{ std::istream_iterator<ColumnProxy>(testCsv), std::istream_iterator<ColumnProxy>() };

    // Print result. Go through all lines and then copy line elements to std::cout
    std::for_each(csv.begin(), csv.end(), [](Columns& c) {
    std::copy(c.begin(), c.end(), std::ostream_iterator<std::string>(std::cout, " ")); std::cout << "\n"; });
    }

    我希望解释足够详细,可以让您了解现代 C++ 可以做什么。

    这个例子基本上不关心有多少行和列。它会吃掉所有东西。

    请不要忘记使用 std::getline 阅读真实文件中的第一行扔掉。

    关于C++:读取文本文件中不确定数量的变量并在struct中修改,然后替换文本文件中的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58586063/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com