Initializing array of objects with data from text file - c++

I’m getting system error when I try to compile the code below on Visual C++ 2008 Express. What I’m trying to do is to initialize array of objects with data read from file. I think there is something wrong inside the while loop, because when I initialize these objects manually without the while loop it seems to work. Here is the code and text file:
#include <iostream>
#include <string>
#include "Book.h"
using namespace std;
int main()
{
const int arraySize = 3;
int indexOfArray = 0;
Book bookList[arraySize];
double tempPrice;//temporary stores price
string tempStr;//temporary stores author, title
fstream fileIn( "books.txt" );
while ( !fileIn.eof( ))
{
getline(fileIn,tempStr);
bookList[indexOfArray].setAuthor(tempStr);
getline(fileIn,tempStr);
bookList[indexOfArray].setTitle(tempStr);
fileIn >> tempPrice;
bookList[indexOfArray].setPrice(tempPrice);
if ( indexOfArray < arraySize ) //shifting array index while not exceeding array size
indexOfArray++;
}
fileIn.close();
return 0;
}
and the text file:
Author1
Book1
23.99
Author2
Book2
10.99
Autho3
Book3
14.56

It looks like you are trying to write to bookList[3] in the loop. You will loop through three times filling your array incrementing indexOfArray each time. This will leave indexOfArray at 3 -- your condition as it is written will allow indexOfAray to be incremented to 3. Then if you have a newline after the "14.56" in your data file you will loop one more time and attempt to pass an empty string to bookList[indexOfArray].setAuthor() leading to a segfault since indexOfArray is past the end of the array.
I would suggest ditching the hard-coded array and using a std::vector instead. At the start of each loop just use push_back() to add a new book to the end of the vector and then use back() to access the new element in the array.

There's another run-time error in your code: You don't read an entire line with the call to fileIn >> tempPrice;. The next call to getline() will read to the end of the line, so you'll get an empty string when you're expecting an author.
You're then off by one line in your text file, and you try to convert a title into a double. That make the fstream signal an error, and after that, you're in trouble.
Brett's right, a vector with push_back is a better solution here.
Brett also correctly pointed out that you could run into errors if your file has extra lines. You can fix that by checking if you successfully read from the file:
if(fileIn >> tempPrice)
{
bookList[indexOfArray].setPrice(tempPrice);
}
else
{
break;
}
if(!getline(fileIn,tempStr))
{
break;
}

The key must be in the contents of
#include "Book.h"
I copy-pasted your code, and replaced the #include with my assumption of what class Book might look like:
class Book
{
std::string auth;
std::string title;
double price;
public:
void setAuthor(std::string& str)
{
auth = str;
}
void setTitle(std::string& t)
{
title = t;
}
void setPrice(double d)
{
d = price;
}
};
and it compiled. Perhaps you could share your Book.h, or look there for any problems? Start with some simple definition from Book (like above) and begin readding code until you've found the lines that cause the problem. Its a crude method of figuring out the issue, but sometimes its the most direct way.

Related

searching a name in the csv file on C++

I am a young programmer who is trying to learn c++. i have a working csv.file. but i want to search for a specific number assigned to the name and then displays the name of what i'm looking for. i have the file here:
1,Bulbasaur,grass
2,Ivysaur, grass
3,Venusaur, grass
4,Charmander, fire
5,Charmeleon, fire
6,Charizard, fire
7,Squirtle, water
8,Wartortle, water
9,Blastoise, water
Code
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream ip("pokedex.csv");
string pokedexnum[9];
string pokemonName[9];
string pokemonType[9];
cout<<"please enter a pokemon number:"<<" ";
cin>>pokemonType[0];
while (ip.good()){
getline( ip, pokedexnum[0]);
getline( ip, pokemonName[0]);
getline( ip, pokemonType[0]);
}
cout<<"the pokemon that is:"<< " "<<pokedexnum[0]<< "is the pokemon called:"<< pokemonName[0];
ifstream close("pokedex.csv");
return 0;
}
when it runs
please enter a pokemon number: 1
the pokemon that is: is the pokemon called:8,Wartortle, water
could you please point out what i am doing wrong?
Among the issues in this code:
You're not using std::getline correctly for comma-separated data. The result is each pass is consuming three lines from your input file; not three values from each line.
You're also not using ip.good() correctly as a while-condition.
You're retaining your test value in the array, which will be overwritten on the first iteration pass, so it is lost.
You're ignoring potential IO failures with each std::getline invoke.
You're overwriting slot-0 in your arrays with each loop iteration.
Minor, ifstream close("pokedex.csv"); clearly isn't doing what you think it is. That just creates another fstream object called close on the given file name.
The later may be intentional for now, but clearly broken in the near future.
In reality, you don't need arrays for any of this. All you're doing is reading lines, and seem to want to test the input number against that of the CSV data first column, reporting the line that you find, then ending this.
So do that:
Read the input value to search for.
Open the file for scanning.
Enumerate the file one line at a time.
For each line from (3), use a string stream to break the line into the comma separated values.
Test the id value against the input from (1). If the same, report the result and break the loop; you're done.
The result is something like this:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cstdlib>
int main()
{
std::cout<<"please enter a pokemon number: ";
long num;
if (std::cin >> num && num > 0)
{
std::ifstream ip("pokedex.csv");
std::string line;
while (std::getline(ip, line))
{
std::istringstream iss(line);
std::string id, name, skill;
if (std::getline(iss, id, ',') &&
std::getline(iss, name, ',') &&
std::getline(iss, skill))
{
char *endp = nullptr;
long n = std::strtol(id.c_str(), &endp, 10);
if (id.c_str() != endp && n == num)
{
std::cout << "The pokemon that is: " << num << " is called: " << name << '\n';
break;
}
}
}
}
}
Admittedly untested, but it should work.
Whether you want to store the items in arrays at this point is entirely up to you, but it isn't needed to solve the somewhat abstract problem you seem to be attempting, namely finding the matching line and reporting the name from said-same. If you still want to store them in arrays, I suggest you craft a structure to do so, something like:
struct Pokemon
{
int id;
std::string name;
std::string skill;
};
and have a single array of those, rather than three arbitrary arrays that must be kept in sync.
Four issues jump out at me:
You store the user's input into pokemonType, but then also use pokemonType for reading data from your CSV file. The file input is going to overwrite the user input.
Your file input loop always references index 0. All of the lines from your data file are going into element 0. That's the main reason that even if the user inputs 1, the output is from the last line of the data file.
Your file reading loop is structured like you want to put one part of each data line into a different array, but what you've written actually reads three lines on every iteration, storing those lines into the three different arrays.
This isn't affecting your output, but the code ifstream close("pokedex.csv"); is written like you want to close the file stream you opened, but I do believe what this line actually does is create a new ifstream called close, and opens pokedex.csv attached to it. In other words, it's just like your other line ifstream ip("pokedex.csv"); but with close as the variable name instead of ip.
You are going to want to look into something called "string tokenization". Start with some web searches, apply what you read about to your code, and of course if you hit another snag, post a new question here to Stack Overflow, showing (as you did here) what you tried and in what way it isn't working.
Elaborating on #3, here's what how your data file is being read:
at the end of the 1st iteration of the file-reading loop, ...
pokedexnum[0] is "1,Bulbasaur,grass"
pokemonName[0] is "2,Ivysaur, grass"
pokemonType[0] is "3,Venusaur, grass"
at the end of the 2nd iteration of the file-reading loop, ...
pokedexnum[0] is "4,Charmander, fire"
pokemonName[0] is "5,Charmeleon, fire"
pokemonType[0] is "6,Charizard, fire"
at the end of the 3rd iteration of the file-reading loop, ...
pokedexnum[0] is "7,Squirtle, water"
pokemonName[0] is "8,Wartortle, water"
pokemonType[0] is "9,Blastoise, water"
And that's why
<< "is the pokemon called:"<< pokemonName[0];
outputs
is the pokemon called:8,Wartortle, water

Invalid argument at memory location when using getline to store a value of a struct in a vector

I've been scouring the forums and internet trying to find a solution to my problem but I can't seem to find anything that relates to me specifically. As a disclaimer, while this relates to homework, the question I'm asking isn't how to do it, it's how to fix a problem in what I already have done.
Anyway, this project is to get an indeterminate amount of values from a file, store them in variables, and output them in a "receipt" layout. I decided the best way to do this would be structs and vectors
struct Item {
string itemName;
double itemPrice;
bool taxable;
}
vector<Item> itemList;
Now that I have my struct and my vector set up in my program, I'm trying to read from the file using getlines.
int vectorNumber = 0;
while(!inFile.eof()){
itemList.resize(vectorNumber+1);
string tempDouble;
string tempBool;
getline(inFile, itemList[vectorNumber].itemName);
getline(inFile, tempDouble);
itemList[vectorNumber].itemPrice = stod(tempDouble);
getline(inFile, tempBool);
itemList[vectorNumber].taxable = tempBool == "Y" ? true : false;
vectorNumber++;
}
The reason I'm doing itemList.resize(vectorNumber + 1) is because I was getting a vector subscript out of range on the first getLine. I figured it was because the vector was blank, but I don't know for sure. Anyway, after I added that line, the program was able to move on, but now on the line with the stod, I'm getting an Unhandled exception at [hexValue] in [exe]: std::invalid_argument at memory location [hexValue].
I added a temporary cout << tempDouble before the faulty stod line to see if it was actually pulling a value, and it is. So my question is why am I getting this weird error?
If I comment out everything from the second getline to the ?: line, the program runs successfully, so it's just this one thing I need help fixing. What am I doing wrong? The value it's pulling from the file (I checked it using cout) is 2.49, with an endline character after it. I know getline discards the endline character, but I figured I'd just tell you.
Thank you!
EDIT: I've been asked to see a minimal version of the code that still gives me problems, so here we are:
// TestApplication.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
struct Item {
string itemName;
double itemPrice;
};
vector<Item> itemList;
int main()
{
ifstream inFile;
inFile.open("HW3_Data.txt");
int vectorNumber = 0;
while (!inFile.eof()) {
itemList.resize(vectorNumber + 1);
string tempDouble;
string tempBool;
getline(inFile, itemList[vectorNumber].itemName);
getline(inFile, tempDouble);
itemList[vectorNumber].itemPrice = stod(tempDouble);
vectorNumber++;
}
}
Because I have a feeling this has to do with the file itself and not the program, here is the file:
Bread
2.49
Y
Milk
1.89
N
Eggs, dozen
0.97
N
Apples
4.75
Y
Bananas
1.69
Y
Peanut Butter
2.49
Y
I copied and pasted the file straight into here. There is another line underneath the Y in the last line of the file, that is not an extra return I added in.

How to read individual lines of a text file using C++

Ok, so its been a while since i messed with reading and writing file and i have just about forgot everything i learned. So, i am currently just trying to figure out how to read specific lines from a text file and output that said line into the command prompt. Here is my code that i am having issues with:
#include <iostream>
#include <fstream>
using namespace std;
int main(){
ifstream input;
int lineN=0;
string line[lineN];
input.open("input.txt");
getline(input, line[lineN]);
cout << line[lineN];
}
As it currently is, it will read the first line of the text file no problem. However, if i change the variable lineN(which stands for line number) to 1 to read the second line, it crashes the prompt. I have no idea what it is i am doing wrong. I have tried researching this problem, but everyone's answer is too vague (That or i'm just too dumb). If you could help me out that would great.
The problem is that you define here an empty array of strings and arrays are not dynamic:
int lineN=0;
string line[lineN];
When you change lineN to 1, nothing changes in the array, and you'll get out of bound !
The bettter way would be to use vectors:
vector<string> line;
Read in a temporary string:
string current_line;
getline(input, current_line);
and add it to your vector:
line.push_back(current_line);
Putting all this in a nice loop would be more useful:
string current_line;
while (getline(input, current_line)) {
line.push_back(current_line);
}
You may access any line later, by using line[i] exactly with your array, as long as i< line.size(). Or you may iterate easily throug all its content:
for (string x : line) { // means for every x in line[]
cout<< x<<endl;
}
you allocate a array of size 0 ...
you will find answer of what will happen can be found here:
C++ new int[0] -- will it allocate memory?

Create an array with external file in C++

I have 4 days of training in C++, so bear with me.
Two data files are required to evaluate a multiple-choice examination. The first file
(booklet.dat) contains the correct answers. The total number of questions is 50. A sample
file is given below:
ACBAADDBCBDDAACDBACCABDCABCCBDDABCACABABABCBDBAABD
The second file (answer.dat) contains the students’ answers. Each line has one student
record that contains the following information:
The student’s answers (a total of 50 answers) in the same format as above (* indicates no answer)., followed by Student ID and Student Name. Example:
AACCBDBC*DBCBDAAABDBCBDBAA*BCBDD*BABDBCDAABDCBDBDA 6555 MAHMUT
CBBDBC*BDBDBDBABABABBBBBABBABBBBD*BBBCBBDBABBBDC** 6448 SINAN
ACB*ADDBCBDDAACDBACCABDCABCCBDDABCACABABABCBDBAABD 6559 CAGIL
I have a homework assignment to write a C++ program that counts the total number of correct answers by each student and outputs this information to another file called report.dat. In this file, the student’s IDs, names and scores must be given. Each correct answer is worth 1 point. For the sample files given above, the output should be as follows:
6555 MAHMUT 10
6448 SINAN 12
6550 CAGIL 49
Here's what I have so far:
include <iostream>
include <fstream>
using namespace std;
int main()
{
char booklet[50] answers[50]
int counter
// Link answers with booklet.dat
booklet = ifstream
input_file("booklet.dat");
return 0;
// Link answers with answers.dat
answers = ifstream
input_file("answer.dat");
return 0;
while (booklet==answers)
{
counter++
cout << "The student had">>counter>> "answers right";
}
}
I'm not even sure I am in the correct direction. I know I need to create an array from the file booklet.dat and another one from the file answer.dat. Then the comparison has to be made and the matches between the two have to be counted.
I don't expect anyone to do the assignment for me, i just need a nudge in the right direction.
1.) On your Syntax:
a) Each line in C++ has to end with an ";". There are some lines in your excample which don't. (Normally your compile should point at this or the following line with an error)
b) Multiple variable definitions need a "," in between two different variables.
2.) I would recommend you to use something like that:
(have a look at C++ Reference fstream)
EDIT: just a little outline, which is not complete in this form, just to give you and idea ;-)
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
int nr_of_students = 1000; /* Or any number you'd like to analyze */
int stud_nr[nr_of_students];
string stud_name[nr_of_students];
int stud_count[nr_of_students];
fstream in_out;
in_out.open("filename.dat",fstream::in); // fstream::in for reading from file
// fstream::out for writing to this file
if(in_out.is_open())
{
for(lines=0;(in_out>>answers && lines<nr_of_students);lines++)
{
in_out >> stud_nr[lines]; /* EDIT: sorry hat some index confusions here... */
in_out >> stud_name[lines];
stud_count[lines]=0;
for(int i=0;i<50;i++)
{
/* comparison between the booklet_array and the answers_array */
/* Count up the stud_count[lines] for each right comparison */
}
}
/* some simmilar code for the output-file */
}
else cout << "Error reading " << "filename.dat" << endl;
return 1;
}
3.) Your code would also get more performance with vectors.
A good Tutorial would be: Tutorial part I
and you find part 2 in the comments there
4.) you can achieve a more dynamic code with argc and argv**, just google for that
I hope these comments help you a little bit to carry on ;)
You are already on the right direction. Basically you want to load the answer key into an array for fast comparison and then you need to check the answers of each student and each time they get a correct answer you increment a counter and write the ID, name and score for each student. There are problems with your code such as missing semicolons.
Also please note that returning exits a function and that no statements after an unconditional return are executed, returning from main terminates your program.
The normal approach to open a file for reading is:
#include<fstream>
#include<string>
int main()
{
std::ifstream input_file("inputfilename");
// since the answer key is one line
// and each students answer , id and name are also one line
// getting that line using std::getline() would be sufficient
std::string line;
std::getline(input_file, line);
// line would now contain the entire first line except the newline character
std::getline(input_file, line);
//now line would now contain the second line in the file
return 0;
}
Writing to a file is similar we use ofstream to open a file for writing.
Like so:
#include<fstream>
int main()
{
std::ofstream output_file("outputfilename");
// lets say we have a string and an int that we want to write
std::string line_to_write("Hello File");
int number = 42;
output_file << line_to_write << number; // writes the string and then 42 on the same line
output_file << '\n'; // writes the newline character so that next writes would appear on another line
return 0;
}
For references to the standard library and C++ in general when you need to know the available functions to do something I recommend cppreference here are the specific pages on ifstream and ofstream.

For loops and inputing data?

trying to figure out how to make a little inventory program and I can't for the life figure out why it isn't working.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct record
{
int item_id;
string item_type;
int item_price;
int num_stock;
string item_title;
string item_author;
int year_published;
};
void read_all_records(record records[]);
const int max_array = 100;
int main()
{
record records[max_array];
read_all_records(records);
cout << records[2].item_author;
return 0;
}
void read_all_records(record records[])
{
ifstream invfile;
invfile.open("inventory.dat");
int slot = 0;
for (int count = 0; count<max_array; count++);
{
invfile >> records[slot].item_id >> records[slot].item_type >> records[slot].item_price >> records[slot].num_stock >> records[slot].item_title >> records[slot].item_author >> records[slot].year_published;
slot++;
}
invfile.close();
}
I'm testing it by having it print the second item from records author. When I run it, it doesn't show the authors name at all. The .dat file is located in just about every folder where the project is (I forgot which folder it needs to be in) so it's there.
The issue isn't that the file isn't working. It's the array not printing off anything.
my inv file is basically:
123456
book
69.99
16
title
etc
etc
and repeats for different books/cds etc all on one line, all without spaces. Should just next in.
You should check to see that the file is open.
invfile.open("inventory.dat");
if (!invfile.is_open())
throw std::runtime_error("couldn't open inventory file");
You should check to seen that your file reads are working and breaks when you hit the end of file.
invfile >> records[slot].item_id >> records[slot].item_type ...
if (invfile.bad())
throw std::runtime_error("file handling didn't work");
if (invfile.eof())
break;
You probably want to read each record at time, as it isn't clear from this code how the C++ streams are supposed to differentiate between each field.
Usually you'd expect to use std::getline, split the fields on however you delimit them, and then use something like boost::lexical_cast to do the type parsing.
If I were doing this, I think I'd structure it quite a bit differently.
First, I'd overload operator>> for a record:
std::istream &operator>>(std::istream &is, record &r) {
// code about like you had in `read_all_records` to read a single `record`
// but be sure to return the `stream` when you're done reading from it.
}
Then I'd use an std::vector<record> instead of an array -- it's much less prone to errors.
To read the data, I'd use std::istream_iterators, probably supplying them to the constructor for the vector<record>:
std::ifstream invfile("inventory.dat");
std::vector<record> records((std::istream_iterator<record>(invfile)),
std::istream_iterator<record>());
In between those (i.e., after creating the file, but before the vector) is where you'd insert your error handling, roughly on the order of what #Tom Kerr recommended -- checks for is_open(), bad(), eof(), etc., to figure out what (if anything) is going wrong in attempting to open the file.
Add a little check:
if (!invfile.is_open()) {
cout<<"file open failed";
exit(1);
}
So that way, you don't need to copy your input file everywhere like you do now ;-)
You are reading in a specific order, so your input file should have the same order and required number of inputs.
You are printing 3rd element of the struct records. So you should have at least 3 records. I don't see anything wrong with your code. It would a lot easier if you can post your sample input file.