C++ program hangs when using cin.getline() - c++

I am making a program keeping track of different persons, which I try to read in from a file. I use a constructor that takes an ifstream file as an argument, and I then try to read in the data from the file. I can read the first line, which is just an int (a unique number for each person), but when I try to go to the next line and getline it, the program hangs. Does anyone know why?
#include <iostream>
#include <fstream>
#include <cstring>
#include <cctype>
#include <cstdlib>
using namespace std;
const int MAXPERS = 100;
const int MAXTXT = 80;
const int DATELEN = 7;
class Person {
private:
int nr;
char* firstName;
char birthDate[DATELEN];
public:
Person() {
char fname[MAXTXT];
cout << "First name: "; cin.getline(fname, MAXTXT);
firstName = new char[strlen(fname) + 1];
strcpy(firstName, fname);
cout << "Birth date (DDMMYY): ";
cin >> birthDate; cin.ignore();
}
Person(int n, ifstream & in) {
nr = n;
char fname[MAXTXT];
cin.getline(fname, MAXTXT);
firstName = new char[strlen(fname) + 1];
strcpy(firstName, fname);
in >> birthDate;
}
void display() {
cout << "\nFirst name: " << firstName;
cout << "\nBorn: " << birthDate;
}
void writeToFile(ofstream & ut) {
ut << firstName << "\n" << birthDate;
}
};
void readFromFile();
Person* persons[MAXPERS + 1];
int lastUsed = 0;
int main() {
readFromFile();
persons[1]->display();
return 0;
}
void readFromFile() {
ifstream infile("ANSATTE.DAT");
if(infile) {
while(!infile.eof() && lastUsed < MAXPERS) {
int nr;
infile >> nr;
persons[++lastUsed] = new Person(nr, infile);
}
}
}
My file looks like this:
1
Andy
180885
2
Michael
230399

In your constructor you have
cin.getline(fnavn, MAXTXT);
So your program is waiting for you to type something in. If you meant to get the name from the file then you need
in.getline(fnavn, MAXTXT);
^^ ifstream object
You are also going to run into the issue of mixing >> with getline. You will need to add
infile.ignore(std::numeric_limits<std::streamsize>::max(), '\n')
after infile >> nr; in your while loop.

strlen(fname + 1) will be strlen(fname) - 1 if fname is one-character long or more and indeterminate if fname is zero-character long. It should be strlen(fname) + 1.
strlen(fnavn + 1) has the same issue and should be strlen(fnavn) + 1.

Related

How to skip reading the first line of file?

How can I ignore the first line of the text file and start at the second line when I called it in the code? I was wondering how. Also, how can I sort the file according to first name, last name and grade? I just have the first name sorted but not the last name and grade accordingly. If you have any idea, I hope you can share it with me. Thanks for the help! Here's my code:
#include <iostream>
#include <fstream>
using namespace std;
struct studentRecord{
string lastname;
string firstname;
string grade;
};
int main(){
ifstream ifs("student-file.txt");
string lastname, firstname, grade, key;
studentRecord records[20];
if(ifs.fail()) {
cout << "Error opening student records file" <<endl;
exit(1);
}
int i = 0;
while(! ifs.eof()){
ifs >> lastname >> firstname >> grade;
records[i].lastname = lastname;
records[i].firstname = firstname;
records[i].grade = grade;
i++;
}
for (int a = 1, b = 0; a < 20; a++) {
key = records[a].firstname ;
b = a-1;
while (b >= 0 && records[b].firstname > key) {
records[b+1].firstname = records[b].firstname;
b--;
}
records[b+1].firstname = key;
}
for (int k = 0; k < 20; k++) {
cout << "\n\t" << records[k].firstname << "\t"<< records[k].lastname << "\t" << records[k].grade;
}
}
When I saw this post it reminded me of a similar task completed at uni. I have rewritten your code to perform the same task but using classes instead of structs. I have also included a way to sort the vector by using the function here.
I have included the "ignore first line" method #Scheff's Cat mentioned.
Here it is:
#include <iostream>
#include <fstream>
#include <sstream>
#include <limits>
#include <string>
#include <vector>
using namespace std;
class studentrecord{
string firstname, lastname, grade;
public:
studentrecord(string firstname, string lastname, string grade){
this -> firstname = firstname;
this -> lastname = lastname;
this -> grade = grade;
}
friend ostream& operator<<(ostream& os, const studentrecord& studentrecord) {
os << "\n\t" << studentrecord.firstname << "\t" << studentrecord.lastname << "\t" << studentrecord.grade;
return os;
}
};
void displayRecords(vector <studentrecord*> records){
for(int i = 0; i < records.size(); i++){
cout << *records[i];
}
}
int main(){
//read in file
ifstream infile;
infile.open("student-file.txt");
infile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
if (!infile.is_open()){
cout << "Error opening student records file" <<endl;
exit(1);
}
vector <studentrecord*> records;
string firstname, lastname, grade;
while (infile >> firstname >> lastname >> grade;) {
records.push_back(new studentrecord(firstname, lastname, grade));
}
displayRecords(records);
return 0;
}
To sort the vector so that it prints in order of either first name, last name or grade I used the following functions:
bool sortfirstname(studentrecord* A, studentrecord* B) {
return (A->getfirstname() < B->getfirstname());
}
bool sortlastname(studentrecord* A, studentrecord* B) {
return (A->getlastname() < B->getlastname());
}
bool sortgrade(studentrecord* A, studentrecord* B) {
return (A->getgrade() < B->getgrade());
}
sort(records.begin(), records.end(), (sortfirstname));
sort(records.begin(), records.end(), sortlastname);
sort(records.begin(), records.end(), sortgrade);
If you wanted to sort by first name you would call the sort(records.begin(), records.end(), (sortfirstname)); function and then the displayrecords() function.
The advantage of using classes stored in vectors is that you don't have to state the size of the vector containing the details about students since you can keep adding information to the end of the vector using the vector.push_back() function. It also makes sorting the data contained easier.
If anything isn't clear, let me know and I can give you a hand.

How to read from file and store in array of objects in c++

I am learning c++ and have a trouble in file handling. I am writing a code as a homework where i have to write objects into a file and then read those objects as array from the file at once. Here is my code:
#include <iostream>
#include <fstream>
using namespace std;
class Records{
char* name;
int roll;
public:
Records()
{
name = new char[20];
}
void setData()
{
cout<<"Enter name: "<<endl;
cin>>name;
cout<<"Enter roll"<<endl;
cin>>roll;
}
char* getname()
{
return name;
}
int getRoll()
{
return roll;
}
void operator = (Records& no)
{
name = no.name;
roll = no.roll;
}
};
int main()
{
int i =0 ;
Records rec;
rec.setData();
Records::increase();
ofstream fout;
fout.open("file.txt", ios::app);
fout.write((char*)&rec, sizeof(rec));
fout.close();
Records* results = new Records[20];
Records rec1;
ifstream fin;
fin.open("file.txt", ios::in);
while(!fin.eof())
{
fin.read((char*)&rec1, sizeof(rec1));
results[i] = rec1;
i++;
}
fin.close();
cout<<results[0].getRoll();
return 0;
}
So basically, I made a Records class and store its object in a file. That works fine but I faced problem while taking data from file. It is not showing anything or sometimes showing garbage value. Anyone have better idea please hep me.
Thanks in advance!
First, you have to open file in binary mode for read and write.
std::ofstream fou("out_filename",std::ofstream::binary);
std::ifstream fin("in_filename", std::ifstream::binary);
Secondly, you assign operator=() is problematical. It assigns two records using the same address. Therefore in the reading process, all 20 elements in result[i] were all sharing the address of rec1::name. You have to correct the operator=() by copying contents of name.
This is not good.
void operator = (Records& no)
{
name = no.name;
roll = no.roll;
}
Rewrite as follows:
Edit: since your objects are all initially assigned with its memory. The new allocation is not necessary.
Records& Records::operator=(const Records& no)
{
// this->name = new char [20];
std::copy_n(no.name, 20, this->name); // include <algorithm>
roll = no.roll;
return *this; // return current object for another =.
}
Finally, add a destructor
Records::~Records() {
delete [] this->name; }
Good luck!
After fixed some other errors, I post this final version for you reference. Note that this project cannot use dynamic allocation for the field "name". Using dynamic allocation, the 20-byte of "name" is not counted as the size of class Records, and the pointer itself is not transferable. It causes read/write error in the field "name".
#include <iostream>
#include <fstream>
#include <algorithm>
using namespace std;
class Records{
char name[20];
int roll;
public:
Records()
{
// name = new char[20];
}
void setData()
{
cout<<"Enter name: "<<endl;
cin>>name;
cout<<"Enter roll"<<endl;
cin>>roll;
}
const char* getname() const
{
return name;
}
int getRoll() const
{
return roll;
}
Records& operator = (const Records& no)
{
std::copy_n(no.name, 20, this->name);
roll = no.roll;
return *this;
}
};
int main()
{
int i =0, c ;
std::string a;
Records rec;
ofstream fout;
fout.open("file.txt", std::ofstream::binary);
c = 0;
while (1)
{
std::cout << "Input record [" << c << "] ? (y/n) ";
std::cin >> a;
if (a[0]=='y' || a[0]=='Y')
{
rec.setData();
fout.write((char*)&rec, sizeof(rec));
++c;
}
else break;
}
fout.close();
// output
Records* results = new Records[20];
Records rec1;
ifstream fin;
fin.open("file.txt", std::ifstream::binary);
while(!fin.eof())
{
fin.read((char*)&rec1, sizeof(rec1));
results[i] = rec1;
i++;
}
fin.close();
// eidt to print all records
for (int j=0; j<(i-1); j++)
{ std::cout << "record # = " << j << std::endl;
std::cout << " name = " << results[j].name;
std::cout << " roll = " << results[j].roll << std::endl;
}
return 0;
}
A test run
$ ./a.exe
Input record [0] ? (y/n) y
Enter name:
aaaa
Enter roll
1234
Input record [1] ? (y/n) y
Enter name:
bbbb
Enter roll
2345
Input record [2] ? (y/n) y
Enter name:
cccc
Enter roll
3456
Input record [3] ? (y/n) n
1234

Read words and numbers from text file (c++)

I have a text with this format:
NAME(char) ID(int) MARK(int)
for example:
JOSH 1234 100
SARA 5678 90
..
..
I want to read this file, enter the params to a students struct and then print it on the screen ( you can see in the code).
My problem is that it seems I can't read the name properly, and this line:
while (file >> id >> word >> grade) - is getting the id and the mark correct, but the name in the variable word (which is char *) is getting hex addresses like 0x000a8520.
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
struct Student
{
int id;
char * name = new char [25];
int mark;
};
int makeStudentsArr(Student * ptr, char * address);
void printArr(Student * ptr, int number);
int main() {
char * address = new char[26];
Student * myptr = new Student[50];
cin >> address;
int count;
count = makeStudentsArr(myptr, address);
printArr(myptr, count);
system("pause");
return 0;
}
int makeStudentsArr(Student * ptr, char * address) {
ifstream file;
file.open(address);
char * word = new char[25];
int id,grade;
int index = 0;
while (file >> id >> word >> grade) {
ptr[index].id = id;
ptr[index].name = word;
ptr[index].mark = grade;
index++;
}
return index;
}
void printArr(Student * ptr, int number) {
for (int i = 0;i < number;i++) {
cout << "name: " << (ptr + i)->name << " id: " << (ptr + i)->id << " mark: " << (ptr + i)->mark << endl;
}
}
Change this:
ptr[index].name = word;
to this:
strcpy(ptr[index].name, word);
since the first copies the pointer, while the second copies the actual string (where the pointer is pointing to)!
However, your program does not delete the memory you dynamically allocated with new, which results in memory leaks.
It would be much easier if you used an std::string instead of C strings. Moreover, it would also be easier, if you used an std::vector instead of plain C arrays.

C++ import data from a .txt file via a string pointer

players.txt
2
Zlatan
Ibrahimovic
1981
4
20130110
20130117
20130122
20130208
Yohan
Cabaye
1986
1
20130301
Main.cpp
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
ifstream pFile("players.txt");
int numberOfPlayers;
string firstName;
string lastName;
int birthYear;
int numberOfMatches = 0;
string *matchDates = NULL;
matchDates = new string[];
while (pFile >> numberOfPlayers >> firstName >> lastName >> birthYear >> numberOfMatches >> matchDates)
{
for(int i = 0; i < numberOfPlayers; i++)
{
cout << firstName << endl;
cout << lastName << endl;
cout << birthYear << endl;
for(int i = 0; i < numberOfMatches; i++)
{
cout << matchDates[i] << endl;
}
}
}
delete[] matchDates;
getchar();
return 0;
}
So I want to import data from a textfile, e.g. soccer players as above or anything else, like hockey players etc. The point is to be able to use this code for all kind of sports aka it's not set to like 12 players max, amount of players should be dynamic. Well I'm already stuck here with what I thought was a dynamic array (matchDates) but I've been told that it's not and is still a static array.
matchDates should also be dynamic because some players could've 20 mathcdates, another could have five, another three and so on. In my example zlatan has four and youhan one.
Well my problem appear on line 20, posted below:
while (pFile >> numberOfPlayers >> firstName >> lastName >> birthYear >> numberOfMatches >> matchDates)
Error message when I hover over the bolded ">>" (which is underlined with red in VS)
Error no operator ">>" matches these operands
operand types are: std::basic_istream> >> std::string *
I want to print out all matchdates but it doesn't work and I'm very sure it's something to do with matchDates.
Thanks in advance and regards Adam
Use C++ class for player, stop using new and override stream operator.
See the code in action here
#include <iostream>
#include <string>
#include <deque>
#include <fstream>
#include <algorithm>
#include <iterator>
using namespace std;
class Player
{
public:
Player() : firstName(""), lastName(""), birthYear(0){}
~Player() {}
//copy ctor by default
// Operator Overloaders
friend ostream &operator <<(ostream &output, const Player p);
friend istream &operator >>(istream &input, Player &p);
private:
string firstName;
string lastName;
int birthYear;
deque<string> matchDates;
};
ostream& operator <<( ostream& output, const Player p )
{
output << p.firstName << " " << p.lastName << " etc ...";
return output;
}
istream& operator >>( istream& input, Player &p )
{
input >> p.firstName;
input >> p.lastName;
input >> p.birthYear;
int nbMatch = 0; input >> nbMatch;
for(int i = 0 ; i < nbMatch ; ++i) {
string match_date; input >> match_date;
p.matchDates.push_back(match_date);
}
return input;
}//*/
int main()
{
//ifstream pFile("players.txt");
int numberOfPlayers;
deque<Player> players;
cin >> numberOfPlayers; int i = 0;
while( i < numberOfPlayers)
{
Player p;
cin >> p;
players.push_back(p);
i++;
}
std::ostream_iterator< Player > output( cout, "\n" );
cout << "What we pushed into our deque: ";
copy( players.begin(), players.end(), output );
getchar();
return 0;
}
The Problem
This is the error Clang gives me:
error: invalid operands to binary expression ('istream' and 'string *')
Then it goes on to list the various overloads of operator>>() and why a right-hand operand of type std::string* cannot convert to respective right-hand types. This is expected because there are no overloads of operator>>() that take a pointer or array of that type.
Solution
When reading into an array, you normally read into "temporary" objects and copy that to the array. For example:
#include <iostream>
int main()
{
int array[5];
for (int i = 0, temp; i < 5 && std::cin >> temp; ++i)
{
array[i] = temp;
}
}
But in your case, you initialized your array with the following line which I assume to be an attempt to create an indefinitely-sized array:
matchDates = new string[];
I don't know about Visual Studio, but on Clang I got the following error:
Error(s):
error: expected expression
int* j = new int[];
^
1 error generated
Dynamically-sized arrays are not possible in C++. If you need an array that will grow in size, consider using a std::vector. You would need to call push_back() to insert elements.
You can put things at one line to ease the code for your eye but don't over do it. Take a 'record' as a one record, so you can say.
An array doesn't have the >> operator.
Would go for user 0x499602D2 his answer but I may not upvote nor comment yet, so I put it here as I already answered. ;)
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
ifstream pFile( "players.txt" );
int numberOfPlayers;
string firstName;
string lastName;
int birthYear;
int numberOfMatches = 0;
while( pFile >> numberOfPlayers )
{
for( int i = 0; i < numberOfPlayers; i++ )
{
pFile >> firstName >> lastName >> birthYear >> numberOfMatches;
cout << firstName << endl;
cout << lastName << endl;
cout << birthYear << endl;
for( int i = 0; i < numberOfMatches; i++ )
{
string date;
pFile >> date;
cout << date << endl;
}
}
}
getchar();
return 0;
}
To maintain the data you can createa a struct (or class). The second while loop is just to print out while the first gets the data.
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
using namespace std;
struct Player
{
string firstName;
string lastName;
int birthYear;
std::vector<string> matchDates;
};
int main()
{
ifstream pFile( "players.txt" );
int numberOfPlayers;
int numberOfMatches = 0;
std::vector<Player> players;
// fill the vector players
while( pFile >> numberOfPlayers )
{
for( int i = 0; i < numberOfPlayers; i++ )
{
// create player struct and fill it
Player player;
pFile >> player.firstName >> player.lastName >> player.birthYear >> numberOfMatches;
for( int i = 0; i < numberOfMatches; i++ )
{
string date;
pFile >> date;
player.matchDates.push_back( date );
}
// add it to the vector
players.push_back( player );
}
}
// print out the values
std::vector<Player>::iterator iterPlayer = players.begin(),
endPlayer = players.end;
while( iterPlayer != endPlayer )
{
Player player = *iterPlayer;
cout << player.firstName << endl;
cout << player.lastName << endl;
cout << player.birthYear << endl;
std::vector<string>::iterator iterDates = player.matchDates.begin(),
endDates = player.matchDates.end;
while( iterDates != endDates )
{
string date = *iterDates;
cout << date << endl;
}
}
getchar();
return 0;
}

Using getline to read data from a file in C++

I want to read from a file two things, the full name (first name and last name)
and age. After that I want to store them in an array then print the data.
However, when I try to reading the first record is read find, but the other two are not.
Please tell me what am I missing.
Thank you.
The content of the file is:
sampleFirst1 sampleLast1
30
sampleFirst2 sampleLast2
25
sampleFirst3 sampleLast3
40
The Output is:
Name: sampleFirst1 Age: 30
Name: Age: 30
Name: Age: 30
Here's my Code:
#include "Person.h"
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream inputFile("data.txt", ios::in);
string full_name;
int age = 0;
int i = 0;
Person personArray[3];
while (i < 3)
{
getline(inputFile, full_name);
personArray[i].set_name(full_name);
inputFile >> age;
personArray[i].set_age(age);
++i;
}
inputFile.close();
printData(personArray, 3);
cout << endl;
return 0;
}
You should not mix getline and the >> operator in that way. Prefer a simpler code like this one :
#include <iostream>
#include <fstream>
#include <array>
#include <string>
using namespace std;
struct Person {
std::string name;
int age;
void set_name(std::string const& i_name) { name = i_name; }
void set_age(int i_age) { age = i_age; }
};
int main()
{
ifstream inputFile("data.txt", ios::in);
std::string first;
std::string last;
int age = 0;
int i = 0;
std::array<Person,3> personArray;
while (i < personArray.size()) {
inputFile >> first >> last >> age;
personArray[i].set_name(first + " " + last);
personArray[i].set_age(age);
++i;
}
inputFile.close();
for(Person const& person : personArray) {
std::cout << "Name: " << person.name << " Age: " << person.age << "\n";
}
std::cout << std::flush;
return 0;
}
Or use only getline and a istringstream to pase the age if you dont want to pay the string concatenation extra cost for the full name.
A lot could be said of the parsing method but that is not the point here.