Program terminating after std::out_of_range while trying to find EOF

Howdy yall,

I am trying to count the number of rows in a .csv file however I have repeatedly run into a problem while running it where it will work until the 2nd to last line and then return an error

1
2
terminate called after throwing an instance of 'std::out_of_range'
  what():  vector::_M_range_check: __n (which is 2179) >= this->size() (which is 2179)


I've tried multiple different things and they have all resulted in the same error. This is my current code:
1
2
3
4
5
6
7
8
9
10
11
12
13
while (!fileInput.eof()){
			
    temp = doc.GetRow<std::string>(j); //grab a row  and store it temporarily (has to be stored due to nature of rapidcsv .GetRow)
		
    allData.push_back(fill_allData(temp)); //puts row in vector 
    j++;//increase count.
		
		
}
		
for (int i = 0; i < allData.size(); i++){
	std::cout << allData[i] << std::endl;
}


Any advice?
Last edited on
Just an observation. eof() is set when an attempt to read data fails. It's not set after an operation. So reading the last row from the file will not set .eof() but attempting to read another row will.
Ok, for sure. I've also tried to use while (EOF != fileInput.peek()) but it didn't work either.
with <filesystem> you can read all the bytes (using the file's size) and stop (not one by one, track what you read!), if using the while ( file>>variable) paradigm is not sufficient. That can get funky using streams because of the whitespace, just be sure you count accurately or use the file pointer position or something like that.
most text files though you just say
while(file >> variable)
if you control the file writing, for text files, its very useful to just dump the counts up front, eg number of rows or number of entries etc as the first value in the file, read that and then iterate that many times.
Last edited on
You haven't shown enough code.

You don't show the code that uses fileInput.

Also, you're not showing the code that throws the exception.

On the actual test, don't treat files as files if you don't have to, the object treats them as streams. A stream has states good, bad and failed. You want to keep processing while it's good. The typically looks like:
1
2
3
while (fileInput) {
    //...
}
 
while (!fileInput.eof()){


Do not loop on (! stream.eof()) or (stream.good()). This does not work the way you expect. The eof bit is set true only after you make a read attempt on the file. This means after you read the last record of the file, eof is still false. Your attempt to read past the last record sets eof, but you're not checking it there. You proceed as if you had read a good record. This will result in reading an extra (bad) record. The correct way to deal with this is to put the >> (or getline) operation as the condition in the while statement.
1
2
3
  while (stream >> var) // or while (getline(stream,var))
  {  //  Good operation
  }
@kbw, I'm sorry I'm not entirely sure I understand. That code does use fileInput, and it is what throws the exception. I've stepped through it using gdb and the exception is thrown during the while loop. However, I accept I could be wrong, and I definitely should have included the initialization of variables. Here is the entire function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 bool read_File(const char* fileName, std::vector <Entry> allData){	
	//create temp vector to add file data into before adding it to struct vect.
	std::vector <std::string> temp;
	int row = 0, j = 0;
	char currChar;
	std::string tempLine;

	
	std::ifstream fileInput;
	fileInput.open(fileName);
	
	//make sure the file opens
	//return error if it doesn't, start unpacking if it is
	if (fileInput.is_open()){
		//open the doc and set specifications.
		//LabelParams(columnHeader,rowHeader) 0 = true, -1 = false
		//SeparatorParams(delimiter, trim, hasCR, quoted line break, remove quotes from cells)
		rapidcsv::Document doc(fileName, rapidcsv::LabelParams(0,-1), rapidcsv::SeparatorParams(',', false, rapidcsv::sPlatformHasCR, true, false));

		//traverse file
		while (fileInput >> tempLine){
			
			temp = doc.GetRow<std::string>(j); //grab a row  and store it temporarily (has to be stored due to nature of rapidcsv .GetRow)
		
			allData.push_back(fill_allData(temp)); //puts row in vector 
			j++;//increase count.
		
		
		}
		
		for (int i = 0; i < allData.size(); i++){
			std::cout << allData[i] << std::endl;
		}
		return true;
		
	}
	
	return false;
}


@AbstractionAnon (love the user name btw)
Ok, for sure! I like the while(stream >> var) . It's getting closer to the end of the file, but it is still throwing the error. When I run it, it still outputs the std::out_of_range . Am I applying your suggestion wrong?
I suspect that L21 should be:

 
while (std::getline(fileInput, tempLine) {


>> only extracts upto a white-space char. getline() extracts to the next \n


Simple code to read lines into a vector:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <iterator>

int main() {
	if (std::ifstream ifs { "test.txt" }) {
		std::vector<std::string> lines;

		for (std::string line; std::getline(ifs, line); lines.push_back(std::move(line)));

		std::cout << "There are " << lines.size() << " lines\n";
	} else
		std::cout << "Cannot open file\n";
}

However, I think the logic is not right for reading the csv file. I think you need code something like (not tried):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
bool read_File(const char* fileName, std::vector <Entry>& allData) {
	if (std::ifstream fileInput { fileName }) {
		rapidcsv::Document doc(fileName, rapidcsv::LabelParams(0, -1), rapidcsv::SeparatorParams(',', false, rapidcsv::sPlatformHasCR, true, false));

		for (size_t r {}, last {doc.GetRowCount()}; r < last; ++r) {
			const auto temp { doc.GetRow<std::string>(r) };

			allData.push_back(fill_allData(temp));
			//allData.push_back(fill_allData(doc.GetRow<std::string>(r)));	//Why not??
		}

		for (const auto& ad : allData)
			std::cout << ad << '\n';

		return true;
	}

	return false;
}


PS. Pass allData by ref as you're changing it!!
Last edited on
Topic archived. No new replies allowed.