Unable to take input from a file containing string variables - c++

I decided to make a phonebook in c++ and decided to take input of names, address and number from a file.
So I made a class called contact and declared the public variables name, address and number.
Using a constructor I initialized these to name="noname"(string), number=0(int), address="no address"(string)
Now my body of main goes as:
int main(){
contact *d;
d= new contact[200];
string name,add;
int choice,modchoice;//Variable for switch statement
int phno,phno1;
int i=0;
int initsize=0, i1=0;//i is declared as a static int variable
bool flag=false,flag_no_blank=false;
//TAKE DATA FROM FILES.....
//We create 3 files names, phone numbers, Address and then abstract the data from these files first!
fstream f1;
fstream f2;
fstream f3;
string file_input_name;
string file_input_address;
int file_input_number;
f1.open("./names");
while(f1>>file_input_name){
d[i].name=file_input_name;
i++;
}
initsize=i;
f2.open("./numbers");
while(f2>>file_input_name){
d[i1].phonenumber=file_input_number;
i1++;
}
f3.open("./address");
while(f3>>file_input_address){
d[i1].address=file_input_address;
i1++;
}
now when I later search for a particular entry by name, the name is displayed correctly but the phoneumber is returned as a garbage value and address as "Noaddress"
I dont understand why this is happening...
In case u want to look at entire code, do let me know....
This is how i search for a particular entry which returns the name if matched but returns garbage for phone number....
cout<<"\nEnter the name";//Here it is assumed that no two contacts can have same contact number or address but may have the same name.
cin>>name;
int k=0,val;
cout<<"\n\nSearching.........\n\n";
for(int j=0;j<=i;j++){
if(d[j].name==name){
k++;
cout<<k
<<".\t"
<<d[j].name
<<"\t"<<d[j].phonenumber
<<"\t"<<d[j].address
<<"\n\n";
val=j;
}
}
Thanks in advance

When your reading the file with the phone numbers
f2.open("./numbers");
while(f2>>file_input_name){
d[i1].phonenumber=file_input_number;
i1++;
}
You store the phone number in the string file_input_name but then you use a different var, file_input_number to store the information in the array d;

Hey guys I figured out the problem....
the problem is that i1 should be set to 0 after the second loop
and that the file taking input numbers should be f2.open("numbers") and not names....silly mistake!!

Since you are using C++, and not C, you should take advantage of the things that come with the language. Don't use arrays to store your data, use a std::vector. That way you don't have to remember how many things you have already put into the vector, because you can always ask the vector to tell you the size().
If I had to read in the three files I would go like this:
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <algorithm>
using std::cin;
using std::cout;
using std::fstream;
using std::string;
using std::vector;
class contact {
public:
string name;
string address;
int phone;
};
void print_contact(const contact &c) {
cout << "name " << c.name << " address " << c.address << " phone " << c.phone << "\n";
}
int main(int argc, char **argv)
{
vector<contact> contacts;
string name;
string address;
int phone;
fstream f1("d:\\names.txt");
fstream f2("d:\\phones.txt");
fstream f3("d:\\addresses.txt");
// note that I am using getline() here.
while (getline(f1, name) && f2 >> phone && getline(f3, address)) {
contact c;
c.name = name;
c.address = address;
c.phone = phone;
contacts.push_back(c);
}
for_each(contacts.begin(), contacts.end(), print_contact);
// for the Windows console window
cout << "Press return to continue ...";
string s;
getline(cin, s);
return 0;
}

Related

Writing a vector of objects to file and then reading it

I'm new to C++ and stackoverflow so forgive me any mistakes in my post ;). I need to create a code, which allows me to fill new objects with data from std::cin and export these objects to binary file later. Also, I need to import objects exported to file at some point. Objects represent users with standard user information like username, ID, lvl etc.
#include <vector>
#include <string>
#include <iostream>
class User {
std::string username;
unsigned int ID, lvl;
public:
User(std::string un, int uID, int ulvl) {
username = un;
ID = uID;
lvl = ulvl;
}
};
int main() {
std::string u_name;
int u_ID,u_lvl;
bool finish = false;
char choice;
std::vector<User> test_user_vec;
do {
std::cout << "Enter username: ";
std::cin >> u_name;
std::cout << "Enter ID: ";
std::cin >> u_ID;
std::cout << "Enter lvl: ";
std::cin >> u_lvl;
test_user_vec.push_back(User(u_name, u_ID, u_lvl));
std::cout << "Do you want to add another user? (y/n)?";
choice = getch();
if (choice == 'y') finish = true;
} while (!finish);
return 0;
}
I assume that test_user_vec stores every object I created while my program is running. My problem occurs when I want to export that vector to file. The purpose of this action is to store objects' data even after my program terminates and import all the data when I run my program again.
I was trying to solve this problem on my own, nothing really came to my mind. While I was looking for some info i found something like this:
#include <fstream>
#include <vector>
#include <string>
int main()
{
std::vector<std::string> v{ "one", "two", "three" };
std::ofstream outFile("my_file.txt");
// the important part
for (const auto &e : v) outFile << e << "\n";
}
I've tested it with <string> and <int> vectors and my variables. It's good until I try to export <object>vector.
Also i found another solution and tried to do something with it on another test code:
class Test {
public:
int number;
float number2;
};
int main(){
Test test1;
test1.number = 122;
test1.number2=12;
std::fstream testfile("test1.bin", std::ios::out | std::ios::binary);
testfile.write((char*)&test1, sizeof(test1));
testfile.close();
//ater writing an object with variables i commented this section
//then uncommented this section and run the program again
std::fstream testfile2("test1.bin", std::ios::in);
testfile2.read((char*)&test1, sizeof(test1));
std::cout << test1.number;
testfile2.close();
return 0;
}
Again, it works, i can read test1.number until I want to use vector of objects, not a single object. With vector of objects my cout printed some random values like 11314123e-03.
I was trying to somehow combine these 2 solutions, but nothing worked out. I would like to have a binary file, because i heard it's faster and has any data protection (i can't just open it in notepad and read the data) I'm new to c++, there is a great chance of me trying to do it reeeeeealy inefficient way, so pls help :D.
Data member getter functions can be added to the User class and used in fstream output operations. This should provide the general idea:
std::string userName;
for (const auto &u : v)
{
outFile.write(u.GetID(), sizeof(int));
outFile.write(u.GetLvl(), sizeof(int));
userName = u.GetName();
outFile.write(username.length(), sizeof(size_t));
outFile.write(userName.data(), username.length());
}
For userName, the length is written to precede the userName string data in the file so that the file can be parsed when read. The binary encoding/convention is designer's decision as there are several options. Another option would be to encode the entire object as a null-terminated string, although this would generally be less size efficient except for the userName string itself.
Note: test_user_vec.push_back(User(u_name, u_ID, u_lvl)); is creating temporary User objects on the stack. As #drescherjm and #RaymondChen pointed out, that is OK, but this is a better alternative: test_user_vec.emplace_back(...);

C++ map empty after inserting data.

Firstly, Happy new year to those who come across my question.
I'm currently learning C++ and I have a class project to complete.
In a nutshell, my code (so far) is supposed to instantiate student objects with details read from a txt file (name, reg-number, and a map of ) and add the student to a list.
I then read a second txt file (consisting of reg-number, course code and mark) and check if there is a match of student reg numbers between whats read and the list.
if there is a match, I should insert the marks read from the txt file into the map (part of the student object), such that afterwards, each student has a map containing the courses taken and marks achieved
I seem to be creating the list fine, then I use a stringstream to read the second file and loop through the list to compare reg-numbers.
if there is a match I then call the add mark method to add marks to the map.
Here's the thing.. If after I completed the map inserting, I loop and print a map of a student, the map is empty. for all students. To confirm this I used map.size().
I have tried many ways to understand and rectify the issue but it seems i'm missing the point of something. Instinct tells me that the add mark method is copying a reference to the variable passed, which is then destroyed by the stringstream in the main method, thus showing no data in the map. unfortunately, I can't change any code within the header files, only implement what's declared.
after reading the std library docs for strings, maps etc, and attempting numerous ways of correcting the behaviour, I'm at a loss.
Any advice would be greatly appreciated so I can continue with the project and better understand what is happening. I have added the files below. there is also a Person base class but I havnt changed this as it consists of only a setter and getter.
Many thanks in advance.
student Header:
#ifndef _STUDENT_H_
#define _STUDENT_H_
#include <string>
#include <map>
#include <stdexcept>
#include "Person.h"
using namespace std;
class NoMarkException: public exception
{
};
class Student: public Person
{ public:
// constructor should initialise name and registration number using arguments
// and initialise marks map to be empty
Student(const string &name, int regNo);
// method to return registration number
int getRegNo() const;
// method to add the mark to the map
// if a mark for the module is already present it should be overwritten
void addMark(const string& module, float mark);
// method to retrieve the mark for a module
// should throw NoMarkException if student has no mark for that module
float getMark(const string &module) const throw (NoMarkException);
private:
int regNo;
map<string, float> marks; // keys are modules, values are marks in range 0.0 to 100.0
// friend function to output details of student to stream
// should output name, regno, and minimum, maximum and average marks on a single line
// if the student has no marks "has no marks" should be output instead of the marks
friend ostream& operator<<(ostream &str, const Student &s);
};
#endif
Student.cpp file:
#include <iostream>
#include "Student.h"
#include "Person.h"
using namespace std;
//constructor makes a student object and initialises the map; marks.
Student::Student(const string &name, int regNo) : Person(name) {
this->name = name;
this->regNo = regNo;
map<string, float> marks;
}
//implemented from the header file. Returns the registration number.
int Student::getRegNo() const {
return regNo;
}
// implemented as per header file request. adds marks to the map. if a mark exists, then it is overwritten.
void Student::addMark(const string &module, float mark) {
marks[module] = mark;
cout << "added: " << marks[module]<< endl;
}
//used to find marks in a map.
float Student::getMark(const string &module) const throw (NoMarkException) {
auto search = marks.find(module);
//line to test the map size after using addMark.
cout << "size after inputted values: "<< marks.size();
return marks[module];
}
main.cpp file
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <list>
#include "Student.h"
using namespace std;
//main method to obtain a file name from a user and read the file.
//method passes each .. to student.cpp
int main() {
//setting up variables required
string fileName;
const string fileEnd = ".txt";
string line;
string line2;
ifstream file;
int reg;
int reg2;
string studName;
string lastName;
float mark;
string module;
list<Student> listOfStudents;
cout << "Please enter a file name to access..." << std::endl;
cin >> fileName;
fileName += fileEnd;
// opening file an reading its contents. values are prepared and sent to the Student constructor. Fills a list
// with student objects created with variables read from the studs.txt file.
//checks file is found and exits the program if not
file.open(fileName);
if (!file) {
cerr << "Unable to open file " << fileName << endl;
exit(1);
}
while (getline (file, line)) {
stringstream stream (line);
stream >> reg >> studName >> lastName;
studName += (' ' + lastName);
cout << "Student: " << studName << " has been created." << endl;
listOfStudents.push_front(Student(studName, reg));
}
file.close();
cout << "The list of students has been created :)" << endl << endl;
cout << "Please enter the name of the next file to open"<< endl;
cout << listOfStudents.size()<<endl;
// opening second file. If file not found, exit with an error code.
// otherwise read each line, separate the three words into variables, then loop through the previously created list
//and look for a match of regNo. if true then call the addMark method to add the module and mark to the student's map.
cin >> fileName;
fileName += fileEnd;
file.open(fileName);
if (!file) {
cerr << "Unable to open file " << fileName << endl;
exit(1);
}
while(getline(file, line))
{
istringstream line_stream(line);
line_stream >> reg2 >> module >> mark;
for(Student stud : listOfStudents){
if(stud.getRegNo() == reg2){//out << "Match reg2: " << reg2 << " with stud: " << stud.getName() <<stud.getRegNo()<< endl;
stud.addMark(module,mark); }
}
}
//testing the get mark method of student class. with a module that is in the map. expecting a result
for(Student s :listOfStudents){
cout << s.getMark("CE151") << endl;
}
return 0;
}
You create a temporary copy of Student each time you're going to addMark, and discard it afterwards. You need not to copy it, but use reference, like this:
for (auto& stud: listOfStudents) /* stud.addMark */
It would also be a good idea to avoid copying Student when you print the results:
for (const auto& stud: listOfStudents) /* stud.getMark */
Aaaand one more thing:
float getMark(const string &module) const throw (NoMarkException);
Dynamic exception specification is deprecated in C++11 and removed in later standards. It is considered to be a bad practice and should be avoided.
The answer by user: grungegurunge is pretty much the one you are looking for. However, after looking at your class. I noticed that in the private section for the member variables you have declared map<string, float> marks which is okay. Yet when I look at your class's constructor after you set the name and regNo it appears that you are declaring another map<string, float> named marks where this one is local to the constructor only and does nothing. You create local memory on the stack with automatic storage, never use it, then it gets destroyed after the object goes out of scope; it is unnecessary to have it declared here.
//constructor makes a student object and initialises the map; marks.
Student::Student(const string &name, int regNo) : Person(name) {
this->name = name;
this->regNo = regNo;
// map<string, float> marks; // this line is not needed.
}

using while loop to create objects [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
The biggest issue I am dealing with is in my main.cpp file however I posted my video.h file and video.cpp file just in case.
I am having a difficult time understanding the concept of reading input and then creating an object.
I have a class Video that contains the member functions needed to create an object. My class works fine . In my main I want to be able to read the input before I create the object rather than creating an object then initializing the variables.
video.h
#ifndef VIDEO_H
#define VIDEO_H
#include<iostream>
#include<string>
using namespace std;
class Video{
public:
// constructor works fine
Video(string name, string link, string comment,
double rating, int stars);
void print();
// member variables
private:
string m_name;
string m_link;
string m_comment;
double m_rating;
int m_stars;
};
#endif
video.cpp
#include<iostream>
#include "video.h"
using namespace std;
Video :: Video (string name, string link, string comment,
double rating, int stars){
m_name = name;
m_link = link;
m_comment = comment;
m_rating = rating;
m_stars = stars;
}
void Video :: print (){
cout << m_name << ", " << m_link << ", " << m_comment << ", " <<
m_rating << ", ";
for(int count = 0; count < m_stars; ++count){
cout << "*";
//cout << endl;
}
cout << endl;
//<< " , " << m_stars << endl;
}
main.cpp
#include <iostream>
#include <string>
using namespace std;
#include "video.h"
int main()
/* My program can create objects and initialize the string variables
if the data is hard-coded when the object is created however I want
read data using cin and a while loop then create an object.
Video video1("Title One", "www.youtube.com/one",
"Comment ONE", 1.1, 1);
Video video2("Title Two", "www.youtube.com/two",
"Comment TWO", 2.2, 2);
video1.print();
video2.print();
*/
{
while (Video >> cin) { //I want to use a while loop here
/* The program needs to read these getlines
and other variables in order to store names or comments
that have spaces */
getline(cin, name); // user enters the name then presses "enter"
getline(cin, link); // user enters the link then presses "enter"
getline(cin, comment); // user enters the comment then presses "enter"
double rating; // user enters the rating then presses "enter"
int stars; // user enters the amount of stars then presses "enter"
Video video_one; /* Once the user enters data for all five members a new object is created */
}
}
I would just do
while(true)
{
getline(cin, name); // user enters the name then presses "enter"
getline(cin, link); // user enters the link then presses "enter"
getline(cin, comment); // user enters the comment then presses "enter"
double rating; // user enters the rating then presses "enter"
cin >> rating;
int stars; // user enters the amount of stars then presses "enter"
cin >> stars;
Video video_one(name,link,comment,rating,stars); /* Once the user enters data for all five members a new object is created */
}
Note:
you can't do this:
while(video>>cin){...}
there is no friend function overloading extraction >> operator. to do What you want to do, overload the extraction operator (>>) inside your Video class :
friend istream &operator>>(istream &input, Video &V)
{
input>> V.m_name>>V.m_link>>V.m_comment...;
return input;
}
and use it like this:
while(cin>>video){...}
Also to read input first then instantiate object,you need a container (a vector or an array) to hold all the new video objects that gets created. Do you know the number of videos that will be entered beforehand? The reason is because arrays are assigned a fixed amount of memory and if the combined amount of new video objects' data entered exceeds the amount of array's memory, it will lead to segmentation fault. There are ways to make your array dynamic but its much easier to use a Vector class since it will automatically increase the container size(get itself more memory) as more video titles are entered. I used a vector called listofVideos. At the end of the while loop you use an iterator to traverse through the vector.
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
typedef struct Video_t{ //I used a struct in this case instead of the class
string m_name;
string m_link;
string m_comment;
int m_stars;
}Video_t;
//#include "video.h"
int main()
{
string buffer;
string name;
string link;
string comment;
double rating;
double stars;
vector<Video_t> listofVideos; //I used a vector in this case to store all the videos.
while (getline(cin,buffer)&&buffer != "end") { //I used a control word to signal end of inputs.
if(buffer.empty());
istringstream is(buffer);
if(is>>name>>link>>comment>>stars)
{
Video_t vid;
vid.m_name = name;
vid.m_link=link;
vid.m_comment = comment;
vid.m_stars = stars;
listofVideos.push_back(vid);
}
}
vector<Video_t>::iterator it;
for(it=listofVideos.begin();it!=listofVideos.end();++it)
cout<<(*it).m_name<<" "<<(*it).m_link<<" "<<(*it).m_stars<<endl;
return 0;
}

How do I correc the runtime error Stack around the variable was corrupted?

I am working on a madlibs program using pointers. When I try to build it does so correctly, but I think their is some trouble with dynamically allocated array used to store the lines from the text file that is read in. The umber that goes in the array is a sentinel value in the file. I also left in the cout statements to show it store the information before the error .Can Any help? The error is the stack around "entries".
//here is my code so far
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
using namespace std;
void header();
//string play_again();
void read_game_file(string **entries, int *num_entries, string **story, int *num_lines);
//string get_user_input(string* entries, int * num_entries);
int main()
{
header();
cout<<endl<<endl;
int num_entries=(NULL);
int *num_lines=(NULL);
string *entries (NULL);
string *story (NULL);
read_game_file( &entries, &num_entries, &story, &*num_lines);
cout<<"doneszo"<<endl;
string get_user_input(string*entries, int * num_entries);
}
void header()
{
cout<<"Hello! Welcome to the game Madlibs."<<endl;
cout<<"The object of the game is to produce something that sounds totally ridiculous."<<endl;
cout<<"So don't think to hard about your answers."<<endl;
cout<<"At the top, you will see a bunch of word descriptions followed by blank spaces."<<endl;
cout<<"Type your word in the blanks. The words should match the descriptions on the left."<<endl;
cout<<"Enter no when you no longer wish to play. Enter yes to continue. Have a great laugh!"<<endl;
}
void read_game_file(string **entries, int *num_entries, string **story, int *num_lines)
{
//Ask user to input file name and opens file
ifstream mad_lib;
string file_name;
cout<<"Please enter the file name with extension: ";
cin>>file_name;
mad_lib.open(file_name);
//Checks to see that file name is valid if not ask for input again
if (!mad_lib)
{
cout<<"File could not be opened. Please try again"<<endl;
cout<<"Please enter the file name with extension: ";
cin>>file_name;
mad_lib.open(file_name);
}
int work;
string line;
mad_lib>>work;
num_entries=&work;
getline(mad_lib,line);
*entries=new string[*num_entries];
cout<<*num_entries<<endl;
string * entry;
for(int i=0; i<*num_entries; i++)
{
entry = new string;
getline(mad_lib,*entry);
entries[i]= entry;
cout<<*(entries[i])<<endl;
}
string work_2;
int work_3;
stringstream ss;
getline(mad_lib,work_2);
ss<<work_2;
ss>>work_3;
cout<<work_2<<endl;
num_lines=&work_3;
*story=new string[*num_lines];
string *entry_2;
for(int j=0; j<=*num_lines; j++)
{
entry_2=new string;
getline(mad_lib,*entry_2);
story[j]= entry_2;
cout<<*(story[j])<<endl;
}
}
Don't use pointers in function arguments until necessary.
Use pass by reference and const instead.
Another question to ask yourself - do you really want read_game_file to "fill" up some data in its arguments? Can you design it better? Here, you have a bunch of variables defined in main, and you expect read_game_file to "fill" them for you.
You should instead encapsulate this functionality within a class. Pass in the file name as argument to the class's ReadFile(const string& filename) method. All of this data should be in the class.
To solve your immediate problem (in the unclean way):
void read_game_file(string **entries, int *num_entries, string **story, int *num_lines);
should be
void read_game_file(vector<string>& entries, int& num_entries, vector<string>& story, int& num_lines);
int main()
{
header();
cout << endl << endl; // spaces help readability!
int num_entries = 0;
int num_lines = 0;
vector<string> entries;
vector<string> story;
read_game_file( &entries, &num_entries, &story, &*num_lines);
cout << "doneszo"<< endl;
string get_user_input(vector<string>& entries, int& num_entries);
}
Use vectors instead of arrays. They're elegant and clean to use.
I'll leave the function for you to complete.

how to extract a number string member from a char array

i want to extract number string values of a char array. Actually I want to extract numbers embeded in file names for some file management. For example if there is a file name as file21 then i want the decimal number 21 from this file name.
How can i extract these values?
I tried the following but it results in an unexpected value. I think it is as a result of the implicit typecasting from the char to int while doing the arthimetic operation.
char * fname;
cout<<"enter file name";
cin>>fname;
int filenum=fname[4]%10+fname[5];
cout<<"file number is"<<filenum;
NOTE:
The filenamse are strictly in the format fileXX, XX being numbers between 01 and 99
You need to subtract '0' to get the decimal value of a digit character:
int filenum=(fname[4]-'0')*10+(fname[5]-'0');
Better yet, you should use atoi:
int filenum = atoi(fname+4);
You're getting undefined behavior because you're never allocating memory for the char* you read into:
char * fname = new char[16]; //should be enough for your filename format
or better yet
char fname[16];
Also, what do you expect:
fname[4]%10+fname[5];
to do? Magically concatenate the numbers?
First, you convert the first char to an int, multiply it by 10, convert the second char to an int and add to the first one. A simple google search for char to int would get you there.
How can i extract these values?
There are an infinite number of ways. One way is to use std::istringstream:
#include <string>
#include <sstream>
#include <iostream>
int main () {
std::string fname;
std::cout << "Enter file name: ";
std::getline(std::cin, fname);
int filenum;
std::istringstream stream(fname.substr(4,2));
if(stream >> filenum)
std::cout << "file number is " << filenum << "\n";
else
std::cout << "Merde\n";
}
Here is the simplest code:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
int filenum;
string fname;
cout<<"enter file name";
cin>>fname;
string str2 = fname.substr(4,2);
istringstream(str2) >> filenum;
cout<<"file number is"<<filenum;
return 0;
}
If your input is that much defined, the simplest solution is scanf:
int main()
{
int theNumber = 0;
scanf("file%d.txt", &theNumber);
printf("your number is %d", theNumber);
}
Check it out in action, reading from char* instead of stdio: http://codepad.org/JFqS70yI
scanf (and sscanf) also throws in checking for proper input format: returns number of fields read successfully. In this case if return value is any other than 1, the input was wrong.