0

I'm making a function importcsv() which takes in a filename and outputs a 2D array. For some reason, whenever I use the following version of importcsv(), the compiler runs smoothly, but the executable always returns a "segmentation fault: 11" error.

typedef vector<vector<double> > matrix;

matrix importcsv(string filename)
{
   ifstream myfile (filename);  //Constructs a stream, and then asssociates the stream with the file "filename"

   matrix contents; // Vector which will store the contents of the stream.
   int i, j;
   while(!myfile.eof())
   {
       if(myfile.get()==','){++j;}
       else if(myfile.get()=='\n'){++i; j=0;}
       else{
        contents[i][j]=2;}
   }
   return contents;
}

Can anyone find the source of the error? btw I have the following header:

#include <fstream>
#include <iostream>
#include <array>
#include <vector>
using namespace std;
  • Where does your matrix acquire the space to store the data? – Baum mit Augen Jun 09 '14 at 19:52
  • via the standard allocator. – David Roberts Jun 09 '14 at 19:53
  • You should read how [vector](http://en.cppreference.com/w/cpp/container/vector) works again. You are writing to a 0x0 matrix, thus the segfault. – Baum mit Augen Jun 09 '14 at 19:56
  • I suggest you use `content.at(i).at(j) = 2;` Once you do that, when your code throws an exception and you've discovered you don't have adequate space reserved for your "matrix", rethink your algorithm, and likely review how `std::vector<>::operator[]` works, which likely isn't how you think. `matrix` has no rows, therefore your code invokes UB. – WhozCraig Jun 09 '14 at 19:56

3 Answers3

2

You are getting "segmentation fault: 11" since you have not allocated memory for contents. contents[i][j] will work only if contents has something in it.

You can divide reading of the file and constructing the matrix into various parts:

  1. Reading all the numbers in a line and treating it as a row of contents.
  2. Reading a number from the line and treating it as a column of a row.

This way, the program can be simplified. This also helps you easily isolate problems when there are any and fix them.

typedef vector<vector<double> > matrix;

double readNextNumber(std::istream& str)
{
   double n = 0.0;
   str >> n;

   // Skip until we get ',' or '\n'
   while (str)
   {
     int c = str.getc();
     if ( c == ',' || c == '\n' || c == EOF )
        break;
   }
   return n;
}

std::vector<double> importRow(std::ifstram& myfile)
{
   std::string line;
   std::vector<double> row;

   // Read a line as a string.
   // Then parse the string using std::istringstream.
   // When you have finished parsing the line, you know
   // you have finished constructing a row of the matrix.
   std::getline(myfile, line);
   if ( myfile )
   {
      std::istringstream str(line);
      while (str)
      {
         double n = readNextNumber(str);
         if (str)
         {
            row.push_back(n);
         }
      }
   }
   return row;
}

matrix importcsv(string filename)
{
   ifstream myfile (filename);  //Constructs a stream, and then asssociates the stream with the file "filename"

   matrix contents; // Vector which will store the contents of the stream.
   while(!myfile.eof())
   {
       std::vector<double> row = importRow(myfile);
       if (myfile)
       {
          contents.push_back(row);
       }
   }
   return contents;
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Great answer, but I'm confused as to how `myfile >> line` will "Read a line as a string", as it stops reading on first encounter of whitespace. I *think* you meant `if (std::getline(myfile, line))` but its hard to tell. – WhozCraig Jun 09 '14 at 21:38
  • @WhozCraig, that's what I meant. Thanks for pointing out the error. Fixed the answer. – R Sahu Jun 09 '14 at 21:46
0

You haven't defined the size of contents. So by default, it will be a vector of 0 element. Therefore the calls to the operator[] will lead to a segmentatin fault.

Mr_Hic-up
  • 389
  • 2
  • 11
-1

Implementing the advice of the others here, the quick fix is to use resize() before reading each value into the array:

//WARNING: THIS PROGRAM UTILIZES C++11
#include <fstream>
#include <iostream>
#include <array>
#include <vector>
#include <cctype>
#include <thread>
using namespace std;
typedef vector<vector<double> > matrix;

matrix importcsv(string filename)
{
    ifstream myfile ("wavelengthtorgb.csv");  //Constructs a stream, and then asssociates the stream with the file "filename".
    matrix contents {{0.0}};
        char nextchar; double data; int i,j;

    while(!myfile.eof())
    {
    myfile.get(nextchar);
        if(nextchar==',')
        {
            ++j; 
            contents[i].resize(j+1); 
            cout<<"encountered a comma."<<" contents is now " <<i+1<<" x "<<j+1<<'\n';
        }
        else if(isspace(nextchar))
        {
            myfile.get(); //You might not need this line - first run with this line, and if there is an error, delete it, and try again.
            ++i; 
            contents.resize(i+1);
            j=0; 
            contents[i].resize(j+1);
            cout<<"encountered a carriage return."<<" contents is now " <<i+1<<" x "<<j+1<<'\n';
        }
        else
        {
            myfile.unget();
            myfile >> data;
            contents[i][j]=data;
            cout<< "encountered a double."<<" contents("<<i<<','<<j<<")="<<data<<'\n';
        }
    }
    return contents;
}