Storing lines of input into a vector - c++

In my program I am trying to take from the user lines of input actually names then storing them into a vector.
I wrote my own code but I got a runtime error telling me that "string subscript out of range".
This is my code
const int LEN = 100;
struct Case{
public:
int No_People;
vector<string> Names;
vector<string> Results;
void Set_Data(){
cin >> No_People;
int Size = No_People;
char Line[LEN];
for (int i = 0; i < Size; i++){
cin.getline(Line, LEN);
Names.push_back(Line);
}
}
}

Personally I would define a class to represent a line. Then you can use stream iterators to load the vector.
class Line
{
std::string line;
public:
// Operator to convert a line back to a std::string
operator std::string const&() const {return line;}
// Friend function to read a line from a stream.
friend std::istream& operator>>(std::istream& in, Line& data)
{
return std::getline(in, data.line);
}
};
int main()
{
int countOfPeople;
std::cin >> countOfPeople;
std::vector<std::string> lines;
std::copy_n((std::istream_iterator<Line>(std::cin)), countOfPeople,
std::back_insert_iterator(lines));
}

There's no need to use a char[] array, use std::string instead, especially given that you already are using it.
Note to OP: cin.getline() is this one:
std::istream::getline(char*, int)
The one you ned to use for std::string's is this one:
std::getline(istream&, string&)
struct Case{
public:
int Size;
vector<string> Names;
vector<string> Results;
void Set_Data(){
std::string temp;
cin >> Size; cin.ignore();
for (int i = 0; i < Size; i++){
std::getline(cin, temp);
Names.push_back(temp);
}
}
}
As far as compile errors go, always:
quote the exact error messgae
tell the line it happened at
show the code that contains the line and the relevant classes/methods

Most probably you are accessing the string using subscript which is out of index. It will be easy to answer if you point at which line you are getting the error.

Related

storing input from text file into 2d array using vectors

So, I need to store the data from the text file into 2d array. I tried using vectors. So here is the sample data from the text file:
START 13
PID 11
CORE 120
SSD 0
CORE 60
SSD 0
CORE 20
SSD 0
I want to store this data as final_vec[x][y]. This is what I tried:
void read_file(const string &fname) {
ifstream in_file(fname);
string line;
vector<string> temp_vec;
vector<vector<string>> final_vec;
while ( getline (in_file,line) )
{
stringstream ss(line);
string value;
while(ss >> value)
{
temp_vec.push_back(value);
}
final_vec.push_back(temp_vec);
}
for (int i = 0; i < final_vec.size(); i++) {
for (int j = 0; j < final_vec[i].size(); j++)
cout << final_vec[i][j] << " ";
cout << endl;
}
}
int main()
{
read_file("test.txt");
return 0;
}
I get error:
main.cpp: In function ‘void read_file(const string&)’:
main.cpp:29:29: error: variable ‘std::stringstream ss’ has initializer but incomplete type
stringstream ss(line);
I am not sure if I am on the right track.
IMHO, a better solution is to model each line as a record, with a struct or class:
struct Record
{
std::string label;
int number;
friend std::istream& operator>>(std::istream& input, Record& r);
};
std::istream& operator>>(std::istream& input, Record& r)
{
input >> r.label;
input >> r.number;
return input;
}
The overloaded operator>> makes the input loop a lot simpler:
std::vector<Record> database;
Record r;
while (infile >> r)
{
database.push_back(r);
}
Rather than have a 2d vector of two different types, the above code uses a 1D vector of structures.

How to read pieces of string into a class array C++

I have an array of dvd from a Video class I created
Video dvd[10];
each video has the property,
class Video {
string _title;
string _genre;
int _available;
int _holds;
public:
Video(string title, string genre, int available, int holds);
Video();
void print();
void read(istream & is, Video dvd);
int holds();
void restock(int num);
string getTitle();
~Video();
};
I'm trying to fill up this array with data from my text file where each info such as the title and genre is separated by a comma
Legend of the seeker, Fantasy/Adventure, 3, 2
Mindy Project, Comedy, 10, 3
Orange is the new black, Drama/Comedy, 10, 9
I've tried using getline(in, line, ',') but my brain halts when its time to insert each line into the dvd array.
I also created a read method to read each word separated by a whitespace but I figured thats not what I really want.
I also tried to read a line with getline, store the line in a string and split it from there but I get confused along the line.
**I can get the strings I need from each line, my confusion is in how to insert it into my class array in the while loop especially when I can only read one word at a time.
I need help on what approach I should follow to tackle this problem.
**My code
#include <iostream>
#include <fstream>
#include <cassert>
#include <vector>
#define MAX 10
using namespace std;
class Video {
string _title;
string _genre;
int _available;
int _holds;
public:
Video(string title, string genre, int available, int holds);
Video();
void print();
void read(istream & is, Video dvd);
int holds();
void restock(int num);
string getTitle();
~Video();
};
Video::Video(string title, string genre, int available, int holds){
_title = title;
_genre = genre;
_available = available;
_holds = holds;
}
void Video::read (istream & is, Video dvd)
{
is >> _title >> _genre >> _available>>_holds;
dvd = Video(_title,_genre,_available,_holds);
}
int Video::holds(){
return _holds;
}
void Video::restock(int num){
_available += 5;
}
string Video::getTitle(){
return _title;
}
Video::Video(){
}
void Video::print(){
cout<<"Video title: " <<_title<<"\n"<<
"Genre: "<<_genre<<"\n"<<
"Available: " <<_available<<"\n"<<
"Holds: " <<_holds<<endl;
}
Video::~Video(){
cout<<"DESTRUCTOR ACTIVATED"<<endl;
}
int main(int params, char **argv){
string line;
int index = 0;
vector<string> tokens;
//Video dvd = Video("23 Jump Street", "comedy", 10, 3);
//dvd.print();
Video dvd[MAX];
dvd[0].holds();
ifstream in("input.txt");
/*while (getline(in, line, ',')) {
tokens.push_back(line);
}
for (int i = 0; i < 40; ++i)
{
cout<<tokens[i]<<endl;
}*/
if(!in.fail()){
while (getline(in, line)) {
dvd[index].read(in, dvd[index]);
/*cout<<line<<endl;
token = line;
while (getline(line, token, ',')){
}
cout<<"LINE CUT#####"<<endl;
cout<<line<<endl;
cout<<"TOKEN CUT#####"<<endl;*/
//dvd[index] =
index++;
}
}else{
cout<<"Invalid file"<<endl;
}
for (int i = 0; i < MAX; ++i)
{
dvd[i].print();
}
}
First, I would change the Video::read function into an overload of operator >>. This will allow the Video class to be used as simply as any other type when an input stream is being used.
Also, the way you implemented read as a non-static member function returning a void is not intuitive and very clunky to use. How would you write the loop, and at the same time detect that you've reached the end of file (imagine if there are only 3 items to read -- how would you know to not try to read a fourth item)? The better, intuitive, and frankly, de-facto way to do this in C++ is to overload the >> operator.
(At the end, I show how to write a read function that uses the overloaded >>)
class Video
{
//...
public:
friend std::istream& operator >> (std::istream& is, Video& vid);
//..
};
I won't go over why this should be a friend function, as that can be easily researched here on how to overload >>.
So we need to implement this function. Here is an implementation that reads in a single line, and copies the information to the passed-in vid:
std::istream& operator >> (std::istream& is, Video& vid)
{
std::string line;
std::string theTitle, theGenre, theAvail, theHolds;
// First, we read the entire line
if (std::getline(is, line))
{
// Now we copy the line into a string stream and break
// down the individual items
std::istringstream iss(line);
// first item is the title, genre, available, and holds
std::getline(iss, theTitle, ',');
std::getline(iss, theGenre, ',');
std::getline(iss, theAvail, ',');
std::getline(iss, theHolds, ',');
// now we can create a Video and copy it to vid
vid = Video(theTitle, theGenre,
std::stoi(theAvail), // need to change to integer
std::stoi(theHolds)); // same here
}
return is; // return the input stream
}
Note how vid is a reference parameter, not passed by value. Your read function, if you were to keep it, would need to make the same change.
What we did above is that we read the entire line in first using the "outer" call to std::getline. Once we have the line as a string, we break down that string by using an std::istringstream and delimiting each item on the comma using an "inner" set of getline calls that works on the istringstream. Then we simply create a temporary Video from the information we retrieved from the istringstream and copy it to vid.
Here is a main function that now reads into a maximum of 10 items:
int main()
{
Video dvd[10];
int i = 0;
while (i < 10 && std::cin >> dvd[i])
{
dvd[i].print();
++i;
}
}
So if you look at the loop, all we did is 1) make sure we don't go over 10 items, and 2) just use cin >> dvd[i], which looks just like your everyday usage of >> when inputting an item. This is the magic of the overloaded >> for Video.
Here is a live example, using your data.
If you plan to keep the read function, then it would be easier if you changed the return type to bool that returns true if the item was read or false otherwise, and just calls the operator >>.
Here is an example:
bool Video::read(std::istream & is, Video& dvd)
{
if (is.good())
{
is >> dvd;
return true;
}
return false;
}
And here is the main function:
int main()
{
Video dvd[10];
int i = 0;
while (i < 10 && dvd[i].read(std::cin, dvd[i]))
{
dvd[i].print();
++i;
}
}
Live Example #2
However, I still say that the making of Video::read a non-static member makes the code in main clunky.

Creating 2D String Vector from text file

I'm having slight trouble creating a 2D Vector of String that's created by reading values from a text file. I initially thought I needed to use an array. however I've come to realise that a vector would be much more suited to what I'm trying to achieve.
Here's my code so far:
I've initialised the vector globally, but haven't given it the number of rows or columns because I want that to be determined when we read the file:
vector<vector<string>> data;
Test data in the file called "test" currently looks like this:
test1 test2 test3
blue1 blue2 blue3
frog1 frog2 frog3
I then have a function that opens the file and attempts to copy over the strings from text.txt to the vector.
void createVector()
{
ifstream myReadFile;
myReadFile.open("text.txt");
while (!myReadFile.eof()) {
for (int i = 0; i < 5; i++){
vector<string> tmpVec;
string tmpString;
for (int j = 0; j < 3; j++){
myReadFile >> tmpString;
tmpVec.push_back(tmpString);
}
data.push_back(tmpVec);
}
}
}
However, when I attempt to check the size of my vector in my main function, it returns the value '0'.
int main()
{
cout << data.size();
}
I think I just need a pair of fresh eyes to tell me where I'm going wrong. I feel like the issues lies within the createVector function, although I'm not 100% sure.
Thank you!
You should use std::getline to get the line of data first, then extract each string from the line and add to your vector. This avoids the while -- eof() issue that was pointed out in the comments.
Here is an example:
#include <string>
#include <iostream>
#include <vector>
#include <sstream>
typedef std::vector<std::string> StringArray;
std::vector<StringArray> data;
void createVector()
{
//...
std::string line, tempStr;
while (std::getline(myReadFile, line))
{
// add empty vector
data.push_back(StringArray());
// now parse the line
std::istringstream strm(line);
while (strm >> tempStr)
// add string to the last added vector
data.back().push_back(tempStr);
}
}
int main()
{
createVector();
std::cout << data.size();
}
Live Example

Dynamically allocate memory for vectors

I am trying to replace arrays with vectors but I can't figure out how.
Replace this function to dynamically allocate memory for vectors:
string readFile(string filename, string** list, int size){
*list = new string[size];
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
*(*list + i) = line;
}
file.close();
return **list;
}
And here's my attempt to change it to vectors with no luck. Any feedback is greatly appreciated:
string processFile(string filename, vector<string>** list, int size){
*list = new vector<string>(size);
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
*list[i] = line; // error
}
file.close();
return **list; // error
}
You will need some proper error handling, but basically, you need neither pointers nor fixed sizes if you use containers:
std::vector<std::string> readLinesFromFile(const std::string& filename)
{
std::vector<std::string> result;
std::ifstream file(filename);
for (std::string line; std::getline(file, line); )
{
result.push_back(line);
}
return result;
}
There are several problems:
You don't need to use vector**, vector is equivalent to the list in previous code.
The return type is string, but you are returning vector**
This code should work, not tested though:
void processFile(string filename, vector<string>& list, int size){
//list = new vector<string>(size); // no need if you get a vector reference
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
list.push_back(line); //the error was because you are assigning string to a vector<string>*
}
file.close();
// you dont have to return, as vector is passed by reference
}
If you still need to use pointer of vector
void processFile(string filename, vector<string>** list, int size){
*list = new vector<string>(size); // bad practice
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
(*list)->push_back(line);
}
file.close();
// you dont have to return, as vector is passed by pointer
}
Change *list[i] = line to *list->push_back(line) and you should be okay for the first error.
The second error is going to depend on what your intent is for the return value. I think return *list->front(); will give the same result as the first example, but if you are planning on returning more than just the first line then you will need to do some concatenation. You can just create a local string and append each line as you read them.
Hopefully your teacher knows using new vector is almost always a code smell in C++ and is using this for a specific reason with a plan to fix it later.
here is a working example. enjoy :)
BTW - you don't need to pass the length, just instantiate the memory and use the push_back method.
#include <vector>
#include <fstream>
#include <string>
using namespace std;
void processFile(string filename, vector<string>** list, int size);
void main()
{
vector<string>* list = NULL;
processFile("C:\\temp.txt", &list, 13);
int i = 1;
}
void processFile(string filename, vector<string>** list, int size){
*list = new vector<string>();
ifstream file(filename);
string line;
for (int i = 0; i < size; i++){
getline(file, line);
(**list).push_back(line); // error
}
file.close();
}

C++ vector with pointer

I am stuck on a homework assignment. I have to read text from a file, allocate each word to memory, then user a pointer to send it to a vector<string*>. My program keeps overwriting the vector with the new word from the file instead of just adding it. I can't figure out why this is happening.
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
void WordFunctions(string *pstr, vector<string*> &words)
{
words.push_back(pstr);
}
int main(){
ifstream file;
vector<string*> a;
string word;
int w =0;
file.open("word.txt");
while (!file.eof())
{
w++;
file >> word;
WordFunctions(&word, a);
}
file.close();
for (int i=0;i<10;i++){
cout<<(*a[i])<<" ";
delete a[i];
}
system ("pause");
}
Either use a vector<string> or allocate the new string on the heap:
void WordFunctions(string *pstr, vector<string*> &words)
{
words.push_back(new string(*pstr));
}
You are pushing the same element into vector which is the address of word. I massage a bit on your code
// pass reference to eliminate copy
void WordFunctions(string &str, vector<string> &words)
{
words.push_back(str);
}
int main(){
ifstream file;
vector<string> a; // you want to store string not the address of the string
string word;
int w =0;
file.open("words.txt");
while (!file.eof())
{
w++;
word.clear(); // clear the content before store something into it
file >> word;
WordFunctions(word, a);
}
file.close();
for (size_t i=0;i<a.size();i++){ // use size instead of hard code magic number
cout<<(a.at(i))<<" "; // use at function instead of []
}
system ("pause");
}
your word string has always the same address in memory, so in the loop you are changing the value of the string, but then you call WordFunctions passing to him always the same address.
If it's a constraint to use vector<string*> instead of vector<string>, you will likely need to allocate memory for new strings in the loop, copy there your word and then pass the new reference to WordFunctions
char *wordPtr
while (!file.eof())
{
w++;
file >> word;
wordPtr = (char *)malloc((strlen(word)+1)*sizeof(char));
strcpy(wordPtr, *word);
WordFunctions(wordPtr, a);
}