【问题标题】:Read a file in c++ by column按列读取 C++ 中的文件
【发布时间】:2021-07-04 08:35:14
【问题描述】:

我是一名学生,这门 C++ 科目对我来说真的很难。我学习了一个关于文件的主题,并获得了一个 50 行 4 列的文件。我尝试使用我的讲师笔记显示文件。这是我尝试的:

#include < iostream >    
using namespace std;  
int main()  {

 FILE* stream = fopen("student.csv", "r");

 char line[1024];

 while (fgets(line, 1024, stream))

{
    
     printf(" %s ",line);

}

}

尽管我无法真正理解它,但我还是设法显示了该文件。有人可以向我解释一下 char 线是做什么用的吗?它代表 50 行吗?如果我想找到一列的最小值,我必须声明一个新变量?

【问题讨论】:

  • 发布的代码不处理列。它一次读取(正确)并打印(错误)整行(行),一行(行)。 char line[1024]; 存储那一行。如果需要将行分成列,则需要更多代码。
  • 小记:&lt; iostream &gt;错了。它应该是&lt;iostream&gt;,否则预处理器可能会搜索以空格字符开头和结尾的文件。
  • 了解每个函数的作用(fopenfgetsprintf)对于理解这个程序的作用很重要。也就是说,如果char line[1024]; 行让您感到困惑,您需要查看更多的补救说明,我建议您使用decent book。最后,不管怎样,C++ 程序员无论如何都不会这样做。他们可能会使用std::getlinestd::string。您在这里展示的是基本 C 工程师可能如何逐行读取文件。
  • 如何将行分成列?
  • 让 C++ 班的老师教授旧的 C I/O 函数似乎很奇怪。老师应该教你标准的 C++ 流。这门课可能不会很好。

标签: c++ csv file


【解决方案1】:

在 C++ 中,您通常会使用 std::string 来读取文件并将其拆分为列。

对不起,我不能“降级”以在 C++ 中使用 char 数组。因此,我假设您使用std::ifstream 打开一个文件,并在循环中使用std::getline 逐行读取。然后您将每一行都放在std::string

然后:

将字符串拆分为多个部分是一项非常古老的任务。有许多可用的解决方案。都有不同的属性。有些难以理解,有些难以开发,有些更复杂、更慢或更快或更灵活或不灵活。

替代品

  1. 手工制作,多种变体,使用指针或迭代器,可能难以开发且容易出错。
  2. 使用旧式std::strtok 函数。也许不安全。也许不应该再使用了
  3. std::getline。最常用的实现。但实际上是一种“误用”,并不那么灵活
  4. 使用专门为此目的开发的专用现代功能,最灵活且最适合 STL 环境和算法环境。但速度较慢。

请在一段代码中查看 4 个示例。

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <regex>
#include <algorithm>
#include <iterator>
#include <cstring>
#include <forward_list>
#include <deque>

using Container = std::vector<std::string>;
std::regex delimiter{ "," };


int main() {

    // Some function to print the contents of an STL container
    auto print = [](const auto& container) -> void { std::copy(container.begin(), container.end(),
        std::ostream_iterator<std::decay<decltype(*container.begin())>::type>(std::cout, " ")); std::cout << '\n'; };

    // Example 1:   Handcrafted -------------------------------------------------------------------------
    {
        // Our string that we want to split
        std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
        Container c{};

        // Search for comma, then take the part and add to the result
        for (size_t i{ 0U }, startpos{ 0U }; i <= stringToSplit.size(); ++i) {

            // So, if there is a comma or the end of the string
            if ((stringToSplit[i] == ',') || (i == (stringToSplit.size()))) {

                // Copy substring
                c.push_back(stringToSplit.substr(startpos, i - startpos));
                startpos = i + 1;
            }
        }
        print(c);
    }

    // Example 2:   Using very old strtok function ----------------------------------------------------------
    {
        // Our string that we want to split
        std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
        Container c{};

        // Split string into parts in a simple for loop
#pragma warning(suppress : 4996)
        for (char* token = std::strtok(const_cast<char*>(stringToSplit.data()), ","); token != nullptr; token = std::strtok(nullptr, ",")) {
            c.push_back(token);
        }

        print(c);
    }

    // Example 3:   Very often used std::getline with additional istringstream ------------------------------------------------
    {
        // Our string that we want to split
        std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
        Container c{};

        // Put string in an std::istringstream
        std::istringstream iss{ stringToSplit };

        // Extract string parts in simple for loop
        for (std::string part{}; std::getline(iss, part, ','); c.push_back(part))
            ;

        print(c);
    }

    // Example 4:   Most flexible iterator solution  ------------------------------------------------

    {
        // Our string that we want to split
        std::string stringToSplit{ "aaa,bbb,ccc,ddd" };


        Container c(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {});
        //
        // Everything done already with range constructor. No additional code needed.
        //

        print(c);


        // Works also with other containers in the same way
        std::forward_list<std::string> c2(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {});

        print(c2);

        // And works with algorithms
        std::deque<std::string> c3{};
        std::copy(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {}, std::back_inserter(c3));

        print(c3);
    }
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多