Why is this code accessing vector out of its range? - c++

So, I'm trying bring over some code to a Qt project I'm working on. The Motion class imports some control points from .txt file into the public member variable ctrlPos using fstream. When I use readCtrlPositions and then try to access ctrlPos with writePositions, for example, I get the error "vector subscript out of range".
There is a lot more code, but hopefully this should be sufficient to answer my question. I'm also a bit of a novice, so with any luck it's not something too stupid.
Motion class header:
#ifndef MOTION_H
#define MOTION_H
#include <vector>
#include "DualQuaternion.h"
class Motion
{
public:
virtual ~Motion();
virtual void readCtrlPositions(char*, char*);
virtual void writePositions(char*);
virtual void drawCtrlPositions();
virtual void set(int, vector<DualQuaternion>);
virtual pair<int, vector<DualQuaternion>> get();
public:
vector<DualQuaternion> ctrlPos, c;
int numberOfPositions;
};
#endif
Motion class:
#include <stdlib.h>
#include <GL\glut.h>
#include "motion.h"
#include "Quaternion.h"
#include "hMatrix.h"
#include "hPoint.h"
using namespace std;
void Motion::readCtrlPositions(char *fileNameArg, char *t)
{
ifstream inFile(fileNameArg, ios::in);
if (!inFile)
{
cerr<<"File" << fileNameArg << "could not be opened" << endl;
exit(1);
}
int i;
inFile >> numberOfPositions;
Quaternion *RotationQuaternion = new Quaternion[numberOfPositions];
for (i = 0; i<numberOfPositions; i++)
inFile >> RotationQuaternion[i];
if (t == "v")
{
Vector *TranslationVector = new Vector[numberOfPositions];
for (i = 0; i<numberOfPositions; i++)
inFile >> TranslationVector[i];
ctrlPos.clear();
for (i = 0; i<numberOfPositions; i++)
{
DualQuaternion dQ(RotationQuaternion[i], TranslationVector[i]);
ctrlPos.push_back(dQ);
cout << "first position from input: " << ctrlPos[i] << endl;
}
delete[] TranslationVector;
}
else if (t == "q")
{
Quaternion *TranslationQuaternion = new Quaternion[numberOfPositions];
for (i = 0; i<numberOfPositions; i++)
inFile >> TranslationQuaternion[i];
ctrlPos.clear();
for (i = 0; i<numberOfPositions; i++)
{
DualQuaternion dQ(RotationQuaternion[i], TranslationQuaternion[i]);
ctrlPos.push_back(dQ);
cout << "first position from input: " << ctrlPos[i] << endl;
}
delete[] TranslationQuaternion;
}
delete[] RotationQuaternion;
}
void Motion::writePositions(char *fileNameArg)
{
ofstream outFile(fileNameArg, ios::out);
if (!outFile)
{
cerr<<"File" << fileNameArg << "could not be opened for writing" << endl;
exit(1);
}
int i;
outFile << numberOfPositions << endl << endl;
for (i = 0; i<numberOfPositions; i++)
outFile << ctrlPos[i].GetReal();
outFile << endl;
for (i = 0; i<numberOfPositions; i++)
outFile << ctrlPos[i].GetDual();
}
void Motion::set(int n, vector<DualQuaternion> p)
{
int i;
numberOfPositions = n;
ctrlPos.clear();
for (i = 0; i<numberOfPositions; i++)
ctrlPos.push_back(p[i]);
}
pair<int, vector<DualQuaternion>> Motion::get()
{
return make_pair(numberOfPositions, ctrlPos);
}
void Motion::drawCtrlPositions()
{
vector <hMatrix> homogeneousMatricesForCtrlPositions;
for (int i=0; i<numberOfPositions; i++)
{
homogeneousMatricesForCtrlPositions.push_back(ctrlPos[i].dualQuaternionToHomogeneousMatrix().transpose());
double MatrixforOpenGLStack[16];
for (int i1=0; i1<4; i1++)
for (int i2=0; i2<4; i2++)
MatrixforOpenGLStack[4*i1+i2] = homogeneousMatricesForCtrlPositions.at(i).m[i1][i2];
::glPushMatrix();
::glMultMatrixd(MatrixforOpenGLStack);
glutSolidTeapot(0.15);
::glPopMatrix();
}
}
Motion::~Motion()
{
}
Sample code where error occurs in Qt program:
static Curve m;
m.readCtrlPositions("input.txt", "v");
m.writePositions("output.txt"); //<--vector subscript out of range
m.readCtrlPositions("output.txt", "q");
ctrlPos = m.get().second;
numberOfPositions = m.get().first;

In readCtrlPositions, t is a char*, so nor t=="v", nor t=="q" will be evaluated to true (it would return true if the two pointers were having the same address). So your function will set numberOfPositions to a non zero value but will never fill ctrlPos vector with any value.
Later, you'll try to access ctrlPos elements from 0 to numberOfPositions (not zero), while ctrlPos vector is empty. That's why you are reported to access the vector out of its range!
Replace char* by std::string is an easy way to fix the problem. If you need to keep the parameter as a char*, then use strcmp to compare string values rather than pointers.
I would also strongly recommend that you remove your numberOfPositions attribute and simply use ctrlPos.size() instead. It would have prevented a crash in this case by guaranteeing your class attributes integrity.

Related

unable to get return data from class in .hpp file

I have 2 files: main.cpp and parser.hpp
I am returning vector<vector> from a member function in class in parser.hpp. However it seems I am not getting anything in my main.cpp from the return value because when I print its size I get 0.
This is my main.cpp:
#include <vector>
#include <cstring>
#include <fstream>
#include <iostream>
#include "parser.hpp"
using namespace std;
int main()
{
ifstream file;
file.open("test.csv");
csv obj;
obj.parse(file);
obj.print_parsed_csv(file);
vector<vector<string>> parsed_csv_data = obj.parse(file);
cout << parsed_csv_data.();
cout << parsed_csv_data.size();
for (int i = 0; i < parsed_csv_data.size(); i++)
{
for (int j = 0; j < parsed_csv_data[i].size(); j++)
cout << parsed_csv_data[i][j] << '\t';
cout << endl;
}
}
This is my parser.hpp
using namespace std;
class csv
{
public:
vector<vector<string>> parse(ifstream &file)
{
string str;
vector<vector<string>> parsed_data;
while (getline(file, str))
{
vector<string> parsed_line;
while (!str.empty())
{
int delimiter_pos = str.find(',');
string word = str.substr(0, delimiter_pos);
// cout << word << " ";
if (delimiter_pos == -1)
{
parsed_line.push_back(word);
break;
}
else
{
str = str.substr(delimiter_pos + 1);
// cout << str << endl;
parsed_line.push_back(word);
}
}
parsed_data.push_back(parsed_line);
}
return parsed_data;
}
void print_parsed_csv(ifstream &file)
{
vector<vector<string>> parsed_csv_data = parse(file);
cout << parsed_csv_data.size();
for (int i = 0; i < parsed_csv_data.size(); i++)
{
for (int j = 0; j < parsed_csv_data[i].size(); j++)
cout << parsed_csv_data[i][j] << '\t';
cout << endl;
}
}
};
I am getting correct cout output in parse() only. print_parsed_csv() in parser.hpp and the cout in main.cpp both are giving 0 as the variable's size.
How do I resolve this?
The first time you call obj.parse the stream object is read from until you get to the end of the file. You need to either reopen the file or reset file to point back to the beginning of the file after reading from it.
You pass the same file variable to each of the three functions below but only the first one works. The first call to obj.parse moves where file is pointing in the input file. When obj.parse exits the first time, file is pointing to the end of the file so when it's used in the subsequent 2 calls, there's nothing to read.
obj.parse(file); // <-- this works fine
obj.print_parsed_csv(file); // <-- this fails
vector<vector<string>> parsed_csv_data = obj.parse(file);fails
// ^^^^^^^^^- this fails
See this question for answers on how to reset the ifstream to the beginning of the file.

How to print certain elements from vector?

I am trying to print out whatever is necessary from my program. What it does is it takes a long list from a text file and sort it based on first choice and GPA and put it into a vector. I manage to sort by First choice and GPA however how can I remove whatever output that isn't necessary?
This is an example of my Txt File (The sequence of each line is 1st choice, 2nd choice, 3rd choice, GPA, Name):
CC,DR,TP,3.8,AlexKong
SN,SM,TP,4,MarcusTan
DR,TP,SC,3.6,AstaGoodwin
SC,TP,DR,2.8,MalcumYeo
SN,SM,TP,3.7,DavidLim
SN,SM,TP,3.2,SebastianHo
SC,TP,DR,4,PranjitSingh
DR,TP,SC,3.7,JacobMa
and so on...
This is my output now (it is a long vector):
TP,DR,SC,4,SitiZakariah
TP,DR,SC,3.9,MuttuSami
TP,DR,SC,3.5,SabrinaEster
TP,DR,SC,3,KarimIlham
TP,DR,SC,3,AndryHritik
SN,SM,TP,4,MarcusTan
SN,SM,TP,3.8,MarcusOng
SN,SM,TP,3.7,DavidLim
SN,SM,TP,3.4,MollyLau
SN,SM,TP,3.2,SebastianHo
SN,SM,TP,3.2,NurAfiqah
SN,SM,TP,2.4,TanXiWei
SC,TP,DR,4,SallyYeo
SC,TP,DR,4,PranjitSingh
SC,TP,DR,3.6,RanjitSing
SC,TP,DR,2.8,MalcumYeo
SC,TP,DR,2.8,AbdulHalim
SC,TP,DR,2.7,AlifAziz
DR,TP,SC,3.9,SitiAliyah
DR,TP,SC,3.9,LindaChan
DR,TP,SC,3.8,SohLeeHoon
DR,TP,SC,3.7,PrithikaSari
DR,TP,SC,3.7,NurAzizah
DR,TP,SC,3.7,JacobMa
DR,TP,SC,3.6,AstaGoodwin
CC,DR,TP,3.9,MuruArun
CC,DR,TP,3.8,AlexKong
CC,DR,TP,3.7,DamianKoh
CC,DR,TP,3.3,MattWiliiams
CC,DR,TP,3.3,IrfanMuhaimin
And this is the output that I need (Basically students with CC as their 1st choice without displaying the 3 options):
3.9,MuruArun
3.8,AlexKong
3.7,DamianKoh
3.3,MattWiliiams
3.3,IrfanMuhaimin
This is my program.
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <algorithm>
using namespace std;
struct greater
{
template<class T>
bool operator()(T const &a, T const &b) const { return a > b; }
};
void main()
{
vector<string> v;
int p = 0;
ifstream File;
File.open("DSA.txt");
if (!File.is_open()) return;
string First;
cout << "Round 1:\n";
while (File >> First)
{
v.push_back(First);
p++;
}
for (int i = 0; i < v.size(); i++)
{
sort(v.begin(), v.end(), greater());
cout << v[i] << endl;
}
}
your last for loop:
for (int i = 0; i < v.size(); i++)
{
sort(v.begin(), v.end(), greater());
cout << v[i].substr(9) << endl;
}
EDIT:
If you want to only display ones with CC as 1st choice you can add if statement to your loop:
for (int i = 0; i < v.size(); i++)
{
if (v[i].substr(0,2) != "CC") continue;
cout << v[i].substr(9) << endl;
}
Also, I noticed another problem in your code. You should not sort the vector at every iteration. You should do it only once before the loop:
sort(v.begin(), v.end(), greater());
for (int i = 0; i < v.size(); i++)
{
if (v[i].substr(0,2) != "CC") continue;
cout << v[i].substr(9) << endl;
}
as I propose in the comment,
since the data is well defined as a structure, you can interpret semantically each row and filter according to that: here is what am talking about
int main()
{
std::vector<std::string> v;
std::string r = "CC,DR,TP,3.9,MuruArun";
std::string delimiter = ",";
std::string token = r.substr(0, r.find(delimiter));
if(token == ??)// compare to what ever you want
{
v.emplace_back(r);
}
cout << "token: " << token << endl;
cout << v.size() << endl;
return 0;
}

Separating multiple inputs from a file into even or odd

I need to take 200 randomizes numbers from a file, separate them into even and odd, and make them show up from lowest to highest in their even or odd parts.
I got the code for making it into even and odd from here but the original way had 10 instead of 200 and made the user input the numbers.
I reworked it into this but I get a repeat of the same number then an error that reads Exception thrown: write access violation with a mark near the odd[oddcnt++] = arr[i];
My code so far
#include <iostream>
#include <fstream>
using namespace std;
class Random
{
private:
int x, arr[200], even[200], odd[200], evncnt = 0, oddcnt = 0, i;
public:
void readFile();
};
void Random::readFile()
{
fstream File("Random.txt");
if (File.is_open())
{
while(File >> x)
{
for (i = 0; i < 200; i++)
{
arr[i] = x;
}
for (i = 0; i < 200; i++)
{
if (arr[i] % 2 == 0)
{
even[evncnt++] = arr[i];
}
else
{
odd[oddcnt++] = arr[i];
}
}
cout << "\n The even numbers are: ";
for (i = 0; i < evncnt; i++)
{
cout << even[i] << "";
}
cout << "\n The odd numbers are: ";
for (i = 0; i < oddcnt; i++)
{
cout << odd[i] << "";
}
}
File.close();
}
}
int main()
{
Random file;
file.readFile();
system("pause");
return 0;
}
Your loops are all wrong. This is how it should look
i = 0;
while (file >> x)
{
arr[i] = x;
i++;
}
for (i = 0; i < 200; i++)
{
if (arr[i]%2==0)
...
}
If you put one loop inside another (like you did) then the inner loop executes fully every time the outer loop executes once. That's not what you want (in this case).
The answer is given already by John. So everything OK.
Additionally, I would like to show you the power of modern C++. Especially with using algorithms. You can write very elegant solutions.
Thers is no loop and only vey few variables.
You will of course not copy and paste it, because you will not understand it fully. But it should give you an idea how such a problem could be analysed, then designed, and then coded.
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <string>
class Random {
std::vector<int> values{};
public:
void readFile(const std::string& fileName);
void printOdds();
void printEvens();
};
void Random::readFile(const std::string& fileName) {
// Open file and check, if it could be opened
if (std::ifstream inputFileStream(fileName); inputFileStream) {
// Clear old content in our class
values.clear();
// Copy the contents from the vile into our vector
std::copy(std::istream_iterator<int>(inputFileStream), {}, std::back_inserter(values));
// sort the values
std::sort(values.begin(), values.end());
}
else { // File could not be opened
std::cerr << "\n*** Error: File could not be opened: " << fileName << "\n\n";
}
}
// Copy all odd values to std::cout
void Random::printOdds() {
std::copy_if(values.begin(), values.end(), std::ostream_iterator<int>(std::cout, " "), [](const int i) { return (i % 2) != 0; });
std::cout << "\n\n";
}
// Copy all even values to std::cout
void Random::printEvens() {
std::copy_if(values.begin(), values.end(), std::ostream_iterator<int>(std::cout, " "), [](const int i) { return (i % 2) == 0; });
std::cout << "\n\n";
}
// Driver code
int main() {
Random r;
r.readFile("r:\\random.txt");
r.printEvens();
r.printOdds();
return 0;
}

Display a table after reading in dimensions and table contents from a text file

I am currently working on a project that displays a table after reading the contents and dimensions of the table from a text file.
The contents of puzzle.txt:
5 5
ferac
asdvb
mfkgt
opemd
welsr
I want my program to read the left number and store it in the variable numRow, and the right number in numCol, then read the letters into the puzzle array. However, when the dimension numbers print, they print as 0 0 instead of 5 5, and the puzzle array only outputs empty box characters.
#include <iostream>
#include <map>
#include <fstream>
#include <cstring>
#include <string>
using namespace std;
char puzzle [numRow][numCol];
void initializePuzzle() {
string storeInput;
int numRow, numCol;
cout << "What is the name of the file?" << endl;
getline(cin, storeInput);
ifstream inFile (storeInput);
inFile.open(storeInput.c_str());
for (int c = 0; c < sizeof(storeInput); c++) {
if (c == 0) {
inFile >> numRow >> numCol;
cout << numRow << ' ' << numCol << endl;
}
}
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
inFile >> puzzle[i][j];
}
}
}
void displayPuzzle() {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
cout << puzzle[i][j];
}
cout << endl;
}
}
int main() {
initializePuzzle();
displayPuzzle();
return 0;
}
You can do this just by using the C++ Standard Library. Try this: (See about std::copy(), std::array and std::vector...)
#include <iostream> // For std::cout, std::endl, etc.
#include <fstream> // For std::ifstream
#include <vector> // For std::vector
#include <iterator> // For std::ostream_iterator
int main() {
std::string file_src;
// Ask for file name...
std::cout << "What is the name of the file? " << std::endl;
std::getline(std::cin, file_src);
// Declare the file stream...
std::fstream reader(file_src);
// Terminate the program with value '1' in case of failure when reading file...
if (reader.fail()) return 1;
// Declaring necessary varibles...
unsigned num_row, num_column;
std::string temporary;
/* Extracting 'num_row' and 'num_column' and declaring a 'std::vector' (which are
better than dynamic arrays in numerous ways) with the dimensions... */
reader >> num_row >> num_column;
std::vector<std::vector<char>> puzzle(num_row, std::vector<char>(num_column));
// Iterating over each line and copying the string where required...
for (auto i = 0; std::getline(reader, temporary, '\n') && i < num_row; i++)
if (!temporary.empty())
std::copy(temporary.begin(), temporary.end(), puzzle[i].begin());
else --i;
// Close the stream...
reader.close();
// Print the resulting vector...
for (auto & elem : puzzle) {
std::copy(elem.begin(), elem.end(), std::ostream_iterator<char>(std::cout, " "));
std::cout << std::endl;
}
return 0;
}
Example:
Input:
puzzle.txt
Output:
f e r a c
a s d v b
m f k g t
o p e m d
w e l s r

How to add integers and strings in the same vector?

I need help for my university homework. i'm still new to this.
Basically i am doing a run-length encoding and i don't know how to add the letter after the counter:
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
void error(std::string str)
{
throw std::runtime_error(str);
}
int main()
{ int counter = 1;
std::string id;
std::vector<int> v;
std::cout << "Enter the data to be compressed: ";
std::cin >> id;
try
{ for(int i = 0; i < id.size(); i++)
{
if(std::isdigit(id[i]))
error("invalid input");
}
std::cout << "The compressed data is: ";
for(int i = 0; i < id.size(); i++)
{
if(id[i] == id[i+1])
{
counter++;
}
else if(id[i]!= id[i+1])
{
v.push_back(counter);
v.push_back(id[i]);
counter=1;
}
}
for(int j = 0; j < v.size(); j++)
std::cout << v[j];
}
catch(std::runtime_error& str)
{
std::cerr << "error: " << str.what() << std::endl;
return 1;
}
return 0;
}
For example if i input aaabbb, the probram should output 3a3b. The problem is that it outputs 397398 97 and 98 being the ascii code for a and b.
i don't know how to put the letter after the counter and for them to be in the same vector.
If you want to serialize as a string try this :
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <sstream>
void error(std::string str) {
throw std::runtime_error(str);
}
int main() {
std::ostringstream stream;
int counter = 1;
std::string id;
std::cout << "Enter the data to be compressed: ";
std::cin >> id;
try {
for (int i = 0; i < id.size(); i++) {
if (std::isdigit(id[i]))
error("invalid input");
}
std::cout << "The compressed data is: ";
for (int i = 0; i < id.size(); i++) {
if (id[i] == id[i + 1]) {
counter++;
} else if (id[i] != id[i + 1]) {
stream << counter;
stream << (char) id[i];
counter = 1;
}
}
std::cout << stream.str() << std::endl;
} catch (std::runtime_error& str) {
std::cerr << "error: " << str.what() << std::endl;
return 1;
}
}
v[j] from std::cout << v[j] is of type int and that is why std::cout writes a number. To write it as a character, you should cast v[j] to char as follows: std::cout << (char)v[j]. In this way, std::cout will use the char specialization, not the int one.
While the other answers might give you the output you need, I believe the idiomatic way to solve this is using a class to hold both the character and its count. There are two obvious choices.
std::pair
Could also be std::tuple if you prefer it for consistency or whatever reason. Save your results in a std::vector<std::pair<char, int>. This saves the information, but to print it you would need to define an appropriate function. Add elements via
v.emplace_back(character, count);
Wrapper Class
If you want to offer some functionality without outside helper classes, define a custom wrapper class such as the following.
class CharacterCount {
private:
char character;
int count;
public:
CharacterCount(char character, int count):
character(character), count(count) {}
explicit operator std::string() const { return std::to_string(count) + character;
// Other helper functions or constructors you require
}
This simplifies printing
for (auto& character_count : v)
std::cout << static_cast<std::string>(character_count);
I believe because std::ostream::operator<< is templated, you cannot get an implicit conversion to std::string to work. I would advise against implicit conversion anyway.
You can use the same emplace_back syntax as before because we offer an appropriate constructor.
So you take your input in a string and ultimately just need to stream this information out, ultimately meaning there's really no reason to store the information in a vector, just output it! You can use find_if with a lambda to find the non-consecutive character (or find_if_not if you prefer.)
for(string::const_iterator finish, start = cbegin(id); start != cend(id); start = finish) {
finish = find_if(start, cend(id), [value = *start](const auto i) { return i != value; } );
cout << distance(start, finish) << *start;
}
Live Example