0

I have written a function that reads an unknown number of data (when in a column) from a file to a vector.

#include <iostream>
#include <vector>
#include <fstream> // file writing
#include <cassert>


void ReadFromFile(std::vector<double> &x, const std::string &file_name)
{
    std::ifstream read_file(file_name);
    assert(read_file.is_open());

    size_t lineCount = 0;
    while (!read_file.eof())
    {
        double temp;
        read_file >> temp;
        x.at(lineCount) = temp;
        if (lineCount == x.size() - 1) { break; } // fixes the out of range exception

        lineCount++;
    }
    read_file.close();
}
int main()
{
    size_t Nx = 7;
    size_t Ny = 7;
    size_t Nz = 7;
    size_t N = Nx*Ny*Nz;

    // Initial Arrays 
    std::vector <double> rx(N);
    std::string Loadrx = "Loadrx.txt";
    ReadFromFile(rx, Loadrx);
}

But the lineCount is incrementing one extra time after the data from the file have been copied into the vector. Is there a more elegant way of fixing that problem than the if statement that I have written?

gnikit
  • 1,031
  • 15
  • 25
  • Use getline() instead. – xtluo Nov 18 '16 at 03:57
  • Use x.push_back() instead of x.at(linecount), pass N direct to ReadFromFile, construct v but leave empty, and reserve N elements. – 2785528 Nov 18 '16 at 04:09
  • @nikjohn you do realize that edits are tracked. The original question did not compile. Nice try. – Matt Nov 18 '16 at 04:20
  • These examples might be helpful: http://stackoverflow.com/documentation/c%2b%2b/7660/iostream-library#t=201611180446580050485 – Sergey Nov 18 '16 at 04:46

2 Answers2

6

I have written a function that reads an unknown number of data (when in a column) from a file to a vector.

One of the most elegant (and, I suppose, idiomatic) ways to read unknown amount of data from a "column" (or otherwise regularly-formatted) file is to use istream iterators:

void ReadFromFile(std::vector<double> &x, const std::string &file_name)
{
    std::ifstream read_file(file_name);
    assert(read_file.is_open());

    std::copy(std::istream_iterator<double>(read_file), std::istream_iterator<double>(),
        std::back_inserter(x));

    read_file.close();
}

Usage:

int main()
{
    // Note the default constructor - we are constructing an initially empty vector.
    std::vector<double> rx;
    ReadFromFile(rx, "Loadrx.txt");
}

If you want to write a "safe" version with a limited number of elements to read, use copy_if:

void ReadFromFile(std::vector<double> &x, const std::string &file_name, unsigned int max_read)
{
    std::ifstream read_file(file_name);
    assert(read_file.is_open());

    unsigned int cur = 0;
    std::copy_if(std::istream_iterator<double>(read_file), std::istream_iterator<double>(),
    std::back_inserter(x), [&](const double&) {
        return (cur++ < max_read);
    });

    read_file.close();
}

Usage is obvious:

ReadFromFile(rx, Loadrx, max_numbers);
Sergey
  • 7,985
  • 4
  • 48
  • 80
  • Maybe, it could be optimized by 'reserving' the vector size in advance, based on the file size? Like in https://stackoverflow.com/questions/15138353/how-to-read-a-binary-file-into-a-vector-of-unsigned-chars – ferdymercury Aug 11 '20 at 11:03
0

Try:

void ReadFromFile(const size_t N, 
                  std::vector<double> &x, 
                  const std::string &file_name)
{
    std::ifstream read_file(file_name);
    assert(read_file.is_open());

    while (true)
    {
        double temp;
        read_file >> temp;  // read something

        if(read_file.eof()) break; // exit when eof

        x.push_back(temp);    
        if (N == x.size()) break;  // exit when N elements
    }
    read_file.close();
}

int main()
{
    size_t N = 10;
    std::vector<double> v;
    v.reserve(N);
    ReadFromFile(v,"Data.txt");
}
2785528
  • 5,438
  • 2
  • 18
  • 20