【问题标题】:How to read space and newline separated integers into a 2D array in C++?如何在 C++ 中将空格和换行符分隔的整数读入二维数组?
【发布时间】:2021-06-05 23:59:12
【问题描述】:

我有一个由空格分隔的数字(在本例中全部小于 100)的 .txt 文件,行中由换行符分隔。像这样的:

 41 53 07 91 44
 52 17 13 03 21

我想将这些数字读入一个二维数组,与它们显示的完全一样,以便空格分隔数组的列,新行分隔行。

我可以让它以字符串的形式读取行,但是我无法分离出单个数字,并让它将它们视为整数。

【问题讨论】:

  • 向我们展示您的代码,以便我们指出问题所在或提示您如何继续。
  • 你有预先知道数组的大小吗?也就是说,您知道有 5 列,还是取决于输入文件?您对 array 的定义是什么(即它必须是一个实际的 arrayint data[100][5],或 动态分配 数组,或者它可以是一个容器如std::vector<int>)
  • talnicolas: 是的,对不起,我会记得包括我下一次的东西(我使用的是“getline”命令,碰巧。大卫:我知道尺寸(20* 20),但我很想编写程序,使其适用于其他大小。动态分配数组的想法对我来说似乎更好。

标签: c++


【解决方案1】:

试试stringstream

【讨论】:

  • 对初学者的回答通常包括一个简单的例子,或者至少详细说明他如何使用你的技术实现他的目标。
  • @talnicolas,是的,但我正要出门,链接指向一个简洁而完整的示例,无论如何 Loki Astari 都涵盖了它。
【解决方案2】:

好的,“完全按照它们出现”的要求意味着您需要一个参差不齐的数组,以防不同行中出现不同数量的列。我会使用std::vector< std::vector<long> >。每个包含的向量对应一行。

因此,每次阅读一行文本时,都会创建一个新的空向量。

在您阅读的行上反复调用strtol,使用push_back 将它们收集到向量中。当输出指针与输入指针相同时(这表示失败,可能是因为你到达了行尾),push_back 整个向量并开始下一行。

类似这样的:

std::vector< std::vector<long> > all_data;

std::string text_row;

while(getline(fin, text_row)) {
    all_data.push_back();
    std::vector<long>& this_row = *all_data.rend();

    const char* p1 = text_row.c_str();
    const char* p2;
    while (1) {
         long num = strtol(p2 = p1, &p1, 0);
         if (p1 == p2) break;
         this_row.push_back(num);
    }

    /* to ignore blank lines, add this code
    if (this_row.empty()) all_data.pop_back();
     */
}

【讨论】:

    【解决方案3】:

    这里其实有两个问题:

    1. 如何识别输入和...
    2. 如何在内存中表示它。

    您谈到了“二维阵列”:这是一个要求还是一个假设? 2x5 尺寸是要求还是只是样品?

    您谈到了文件布局。它是强制性的,还是不是?您必须承认(并检查)最终会产生误导,例如更多的数字或错位吗?如果该行第一个有 6 个数字,第二个有 4 个,你该怎么办?

    一个非常简单的解决方案是使用一个 2x5 数组并用一个保护循环填充它:

    #include <iostream>
    #include <fstream>
    #include <iomanip>
    #include <stdexcept>
    
    const int ROWS=2;
    const int COLS=5;
    
    int main(int argc, char** argv)
    {
        int m[ROWS][COLS];
        try
        {
            std::ifstream s(argv[1]);
            if(!s) throw std::runtime_error("cannot open file");
            for(int r=0; r<ROWS; ++r)
                for(int c=0; c<COLS; ++c)
                   if(!(s >>m[r][c])
                       throw std::runtime_error("insufficient or bad input");
        }
        catch(const std::exception& e)
        {
            std::cout << "Reading error: " << e.what() << std::endl;
            return -1;
        }
    
        std::cout << "read matrix is \n";
        for(int r=0; r<ROWS; ++r)
        {
            for(int c=0; c<COLS; ++c)
                std::cout << std::setw(8) << m[r][c];
            std::cout << '\n';
        }
        std::cout << std::endl;
        return 0;
    }
    

    这将读取由“空格”分隔的 10 个数字,无论其分布和对齐方式如何。 (假设 2x5 是一个约束。)

    另一方面,您可能需要自己检测矩阵的宽度:文件可以有任意数量的行,每行的元素长度不限。

    在这种情况下你需要一个“灵活的结构”,你需要识别线条和线条中的元素。

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <vector>
    #include <sstream>
    #include <iomanip>
    #include <stdexcept>
    #include <utility>
    
    int main(int argc, char** argv)
    {
        std::vector<std::vector<int> > m;
        try
        {
            std::ifstream fs(argv[1]);
            if(!fs) throw std::runtime_error("cannot open input file");
            while(fs)
            {
                std::string line;
                std::getline(fs,line);
                std::vector<int> row;
                std::stringstream ss(line);
                int x;
                while(ss >> x)
                   row.push_back(x); 
                if(!row.empty())
                    m.emplace_back(std::move(row));
            }
        }
        catch(const std::exception& e)
        {
            std::cout << "Reading error: " << e.what() << std::endl;
            return -1;
        }
    
        std::cout << "read lines: \n";
        for(auto i=m.begin(); i!=m.end(); ++i)
        {
            for(auto j=i->begin(); j!=i->end(); ++j)
                std::cout << std::setw(8) << *j;
            std::cout << '\n';
        }
        std::cout << std::endl;
    
        return 0;
    }
    

    【讨论】:

    • 虽然您通过检查循环内流的状态来补偿while(fs)while(ss)。这不是从流中读取的惯用方式:尝试while(ss &gt;&gt; x),它从流中读取整数。失败时不会进入循环。
    • 你也可以while(std::getline(fs, line)).
    【解决方案4】:

    试试这个:

    #include <vector>
    #include <string>
    #include <fstream>
    #include <sstream>
    #include <iostream>
    
    int main()
    {
        // The result of the read is placed in here
        // In C++, we use a vector like an array but vectors can dynamically grow
        // as required when we get more data.
        std::vector<std::vector<int> >     data;
    
        // Replace 'Plop' with your file name.
        std::ifstream          file("Plop");
    
        std::string   line;
        // Read one line at a time into the variable line:
        while(std::getline(file, line))
        {
            std::vector<int>   lineData;
            std::stringstream  lineStream(line);
    
            int value;
            // Read an integer at a time from the line
            while(lineStream >> value)
            {
                // Add the integers from a line to a 1D array (vector)
                lineData.push_back(value);
            }
            // When all the integers have been read, add the 1D array
            // into a 2D array (as one line in the 2D array)
            data.push_back(lineData);
        }
    }
    

    【讨论】:

    • 谢谢,这个答案对我来说很有意义。我猜你的意思是 lineData.push_back(value) 在从最后一个执行的行开始的第二个,它工作得很好。更重要的是,我想我明白为什么了!所以我认为字符串流上的 >> 运算符提取一个数字直到它到达空格,然后将自身移动到准备在未来提取它的下一个数字的开头是正确的吗?
    • @SamElliott:不完全是。运算符>> 应用于整数时将: 1) 从流中删除所有空格。 2)如果第一个非空白字符不是整数,它会将流标记为坏,否则它将读取下一个组成整数的'n'个字符(停在第一个非整数字符(可能是空白))
    • 对于 C++ 新手,您还需要包含 stringiostreamsstream 标头。
    【解决方案5】:

    以下代码显示了如何解决您的问题。它还展示了如何在打开文件时使用 RAII。这是获取资源时的好习惯。通过在构造函数中获取资源并在析构函数中释放它,如果抛出异常,可以防止资源泄漏。这在 C++ 世界中被认为是良好的做法。

    #include <fstream>
    #include <vector>
    
    struct FileHandle
    {
      std::ifstream file_;
      FileHandle(std::string file_name)
      {
        file_.open(file_name);
      }
      ~FileHandle()
      {
        file_.close();
      }
    };
    
    bool next_char_is_end_of_line(std::ifstream &file)
    {
      bool found = false;
      char c;
      file.get(c);
      if(c == '\n')
        found = true;
      file.unget();
      return found;
    }
    
    int main()
    {
      FileHandle fh("c:\\your_file.txt");
      std::vector<std::vector<int> > v;
      std::vector<int> current_line;
      int x;
      while(fh.file_ >> x)
      {
        current_line.push_back(x);
        if(next_char_is_end_of_line(fh.file_))
        {
          v.push_back(current_line);
          current_line.clear();
        }
      }
      //Then just access each std::vector<std::vector<int>> 
      //v[0].size(); ...
      return 0;
    }
    

    此代码通过将每行上的所有数字放入一个单独的向量中,为您提供了一个“锯齿状”向量。然后将该向量添加到主向量。所以“向量”中的每一行都可以有不同的长度。我想你明白了……

    祝你好运!

    【讨论】:

    • 虽然我喜欢你对 RAII 的热情,但 std::fstream 已经做到了。构造函数可以接受一个字符串,而析构函数会自动调用 close() (但它也会捕获并丢弃你没有的异常)。
    • @LokiAstari 我完全错过了 fstream 这样做。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2012-12-29
    • 2016-12-09
    • 2013-01-16
    • 2021-11-01
    • 2020-11-16
    • 2021-02-17
    • 1970-01-01
    相关资源
    最近更新 更多