简体   繁体   中英

How to read space and newline separated integers into a 2D array in C++?

I have a .txt file of numbers (in this case all less than 100) separated by spaces, in rows separated by new lines. Something like this:

 41 53 07 91 44
 52 17 13 03 21

I would like to read these numbers into a 2d array, exactly as they appear, so that spaces separate columns of the array, and new lines separate rows.

I can get it to read the lines in as strings, but then I'm having trouble separating out individual numbers, and getting it to treat them as integers.

Try this:

#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);
    }
}

Ok, the "exactly as they appear" requirement means that you need a ragged array, in case different number of columns appear in different rows. I would use std::vector< std::vector<long> > . Each contained vector corresponds to one row.

So, each time you read a row of text, create a new empty vector.

Call strtol repeatedly on the row you read, using push_back to collect them into the vector. When the output pointer is the same as the input pointer (this indicates failure, probably because you reached the end of the line), push_back the whole vector and start the next row.

Something like this:

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();
     */
}

The following code shows how to solve your problem. It also shows how you can use RAII when opening a file. This is good practice when acquiring resources. By acquiring the resource in the constructor and releasing it in the destructor a resource leak can be prevented if an exeption is thrown. This is considered good practice in the C++ world.

#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;
}

This code gives you a "jagged" vector by putting all numbers on each row into a separate vector. This vector is then added to main vector. So each line in the "vector" can have different lengths. I think you get the idea...

Good luck!

试试stringstream

There are actually two problems, here:

  1. how to recognize the input and ...
  2. how to represent it in memory.

You spoke about "2d Array": is this a requirement or just an hypothesis? is the 2x5 size a requirement or just a sample?

You spoke about a file layout. It that mandatory, or not? Must you admit (and check) eventually misleading like more numbers, or misalignment? What do you have to do in case the row have 6 numbers the first and 4 the second?

A very simple solution can be use a 2x5 array and fill it up with a guarded loop:

#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;
}

This will read 10 numbers separated by "blanks", no matter how distributed and aligned. (it is assumend the the 2x5 is a consrain.)

On the other end, you may have to detect yourself how wide the matrix is: the file can have any number of lines, each whatever number of elements long.

In this case you need a "flexible structure", and you need to identify lines and elements in the lines.

#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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM