0

I am trying to read the data from a text file and store it into array. I need it for solving FEM problem. Let's say my text file is as follows:

node: 1,2,3,4,5,6,7,8,9,10
x: 4,4,3.75,3.76773151,3,3.59192947,4,3.5,3.55115372,3.375, 3.71330586 
y: 3,275,3,2.65921885,2.79192947,2.5,3,2.55115372,2.78349365,2.36222989 
z: 0,0,0,0,0,0,0,0,0,0                      

I want to store this data from text file into a 10*4 matrix (myarray[10][4]). Also I need to store each column of this array into a vector. Let's say my vectors are:

double x[10];
double y[10];
double z[10];

for (int i = 0; i < 10; i++)
{
    x[i] = myarray[i][1];
    y[i] = myarray[i][2];
    z[i] = myarray[i][3];
}

I wrote the code like this:

int main()
{
    string line;
    string coordinate[10][4];
    ifstream mesh("mesh.txt");

    for (int i = 0; i < 10; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            if (getline(mesh, line, ';'))
            {
                coordinate[i][j] = line;
                cout << coordinate[i][j] << endl;
                cout << "mesh : " << line[0] << endl;
            }
        }
    }
    mesh.close();
}

Now my problem is when I want to put the each column of coordinate into a vector I get this error:

no suitable conversion function from string to double exist

I don't understand this error, and need help fixing it.

paddy
  • 60,864
  • 6
  • 61
  • 103
  • 2
    Tip: Use `std::vector` instead of C arrays if you can. `push_back` saves a ton of trouble. You also want to use something like `std::tuple` to create your x/y/z coordinates, or maybe even make a simple `struct point { double x,y,z; }` – tadman Apr 15 '21 at 02:38
  • Hint: `x[i]=myarray[i][1]` isn't going to happen. You **must** [convert your string to a `double`](https://stackoverflow.com/questions/4754011/c-string-to-double-conversion), as in `x[i] = std::stod(myarray[i][1])`. – tadman Apr 15 '21 at 02:40
  • 1
    Reminder: C++ arrays are zero-indexed. You're skipping index 0, and using 1-3. – tadman Apr 15 '21 at 02:41
  • Hi thanks for answering. Could you please show me in the code which part I should change? –  Apr 15 '21 at 02:55
  • Why x[i]=myarray[i][1] is not going to happen? I am new in c++, I can't put one column of a matrix into a vector? –  Apr 15 '21 at 03:01
  • You use `if (getline(mesh, line, ';'))` with a delimiter of semicolon but the file doesn't contain any semicolons... – Jerry Jeremiah Apr 15 '21 at 03:19
  • If I delete the ; and usestd::stod(myarray[i][1]) it's gonna be alright? –  Apr 15 '21 at 03:29
  • In C++ there are a limited number of [implicit conversions](https://en.cppreference.com/w/cpp/language/implicit_conversion) defined. A good example is `int` to `double` and vice-versa. There is no such path for `std::string` to `double`. In other words, you must use tools like `std::stod` to explicitly convert. – tadman Apr 15 '21 at 03:37

1 Answers1

0

An iterative way may like this, use the splitter in a customized iterator class. It seems to be complicated but I think it's easy to maintain. Note that the iterator class is a modified version of this great answer.

#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include <vector>

template <typename T>
class istream_line_iterator : public std::iterator<std::input_iterator_tag, T> {
  std::istream* stream;

 public:
  // Creating from a stream or the end iterator.
  istream_line_iterator(std::istream& s) : stream(&s) { dropLeadingSpace(); }
  istream_line_iterator() : stream(nullptr) {}

  // Copy
  istream_line_iterator(istream_line_iterator const& copy)
      : stream(copy.stream) {}
  istream_line_iterator& operator=(istream_line_iterator const& copy) {
    stream = copy.stream;
    return *this;
  }

  // The only valid comparison is against the end() iterator.
  // All other iterator comparisons return false.
  bool operator==(istream_line_iterator const& rhs) const {
    return stream == nullptr && rhs.stream == nullptr;
  }
  bool operator!=(istream_line_iterator const& rhs) const {
    return !(*this == rhs);
  }

  // Geting the value modifies the stream and returns the value.
  // Note: Reading from the end() iterator is undefined behavior.
  T operator*() const {
    T value;
    (*stream) >> value;
    return value;
  }
  T* operator->() const;  // Not sure I want to implement this.

  // Input streams are funny.
  // Does not matter if you do a pre or post increment. The underlying stream
  // has changed. So the effect is the same.
  istream_line_iterator& operator++() {
    dropLeadingSpace();
    return *this;
  }
  istream_line_iterator& operator++(int) {
    dropLeadingSpace();
    return *this;
  }

 private:
  void dropLeadingSpace() {
    // Only called from constructor and ++ operator.
    // Note calling this on end iterator is undefined behavior.

    char c;
    while ((*stream) >> std::noskipws >> c) {
      if (c == '\n') {
        // End of line. So mark the iterator as reaching end.
        stream = nullptr;
        return;
      }
      if (!std::isspace(c) && c != ',') {
        // Found a non space character so put it back
        stream->putback(c);
        return;
      }
    }
    // End of stream. Mark the iterator as reaching the end.
    stream = nullptr;
  }
};

int main() {
  std::ifstream ifs("1.in");
  std::string line;
  std::vector<std::vector<double>> vec;
  while (std::getline(ifs, line)) {
    std::istringstream iss(line);
    std::string pre;
    iss >> pre;

    std::vector<double> line_vec;
    auto beg = istream_line_iterator<double>(iss);
    auto end = istream_line_iterator<double>();

    std::copy(beg, end, std::back_inserter(line_vec));
    vec.push_back(std::move(line_vec));
  }
  for (const auto& inner : vec) {
    for (auto d : inner) {
      std::cout << d << ' ';
    }
    std::cout << std::endl;
  }

  return 0;
}

prehistoricpenguin
  • 6,130
  • 3
  • 25
  • 42