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

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;
}

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.

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

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.

C++ and output on files

I was trying to do a C++ program who do a thing like this:
in a list of surname and name (file), read the same surname and stamp in another file 'people with surname surname are: name,name,name etc..
That's is what i did, but something went wrong
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct persona
{
string nome;
string cognome;
};
int carica_vettore (persona a [])
{
string name, surname;
int i=0, np=0;
ifstream fin ("people.txt");
while (cin >> name >> surname)
{
if (name == "0" && surname == "0")
break;
a[i].nome = name;
a[i].cognome = surname;
i++;
np++;
}
return np;
}
int main ()
{
string nomepers, cognomepers;
int cont = 0;
persona persone [50];
cont = carica_vettore(persone);
for (int k = 0; k < cont; k++)
{
for (int j = k+1; j < cont-1; j++)
if (persone[k] == persone[j])
cout << "People with surname (" << persone[i] << "), are: " << persone[i].nome;
}
return 0;
}
And also i forget to stamp on file, but i think that's is not such a big problem
So, anyone could help me?
2 'for' who check if there is any same surnames btw
Didn't you mean to read from a file?
ifstream fin("people.txt");
while(cin >> name >> surname) // cin reads from the console, waits for your input
Replace cin with fin.
You also don't have operator== for persona overloaded:
bool operator==(persona const& other) const
{
return nome == other.nome && cognome == other.cognome;
}
And also operator<<, which is required here:
cout << "People with surname (" << persone[i] << ...
Print persone[i].nome or persone[i].cognome instead.
if (persone[k] == persone[j]) is the problem. The error message tells you what is wrong and what the line number is. you are comparing two Persona structs, but you cannot use the == operator on them. maybe you should compare their nome or cognome values instead.

error reading data from input file to array

The input file contains 14 state initials (TN,CA,NB,FL,etc..) that is to be rad into the array. The code below clears compiler but when i tell the program the filename it shoots out a bunch of blank spaces with two spaces containing some fuzz and a third contain a '#' symbol. i assume the problem is with my function not entirely sure what specifically though any help greatly appreciated!
input file set up with state initials one on top of the other:
TN
PA
KY
MN
CA
and so on
void readstate( ifstream& input, string []);
int main()
{
string stateInitials[14];
char filename[256];
ifstream input;
cout << "Enter file name: ";
cin >> filename;
input.open( filename );
if ( input.fail())
{
cout << " file open fail" << endl;
}
readstate ( input, stateInitials);
input.close();
return (0);
}
void readstate ( ifstream& input, string stateInitials[])
{
int count;
for ( count = 0; count <= MAX_ENTRIES; count++)
{
input >> stateInitials[count];
cout << stateInitials[count] << endl;
}
}
You are treating a character array as though it were a string array.
While you can hack put the strings next to each other inside the same char array, that is not the standard way it is done. Here is a modified version of your code, which creates one char[] to hold each initial.
#include <iostream>
#include <fstream>
#include <string>
#include <stdlib.h>
#include <string.h>
#define MAX_ENTRIES 14
using namespace std;
void readstate( ifstream& input, char* []);
int main()
{
char** stateInitials = new char*[14];
char filename[256];
ifstream input;
cout << "Enter file name: ";
cin >> filename;
input.open( filename );
if ( input.fail())
{
cout << " file open fail" << endl;
}
readstate ( input, stateInitials);
// After you are done, you should clean up
for ( int i = 0; i <= MAX_ENTRIES; i++) delete stateInitials[i];
delete stateInitials;
return (0);
}
void readstate ( ifstream& input, char* stateInitials[])
{
int count;
string temp_buf;
for ( count = 0; count <= MAX_ENTRIES; count++)
{
stateInitials[count] = new char[3];
input >> temp_buf;
memcpy(stateInitials[count], temp_buf.c_str(), 3);
cout << stateInitials[count] << endl;
}
}

Issues with while loop and reading from files

I'm working on this project and I'm fairly new to C++. Its kind of hard to explain what I'm trying to do but I shall try. So I'm working with a file called flix.txt and in it looks like the following:
1 A 5
1 B 4
1 D 3
1 F 5
2 A 1
3 E 3
3 F 1
4 A 2
The first column are people(my objects), second columns are movies, and the third are the ratings given by the objects.
I'm trying to first extract the first int from every line and create an object using an 'operator new'. Then I'm taking a movie and turning it into an int so I can plug the rating into an array. Sorry if it sounds confusing. Heres the code I have now:
//flix program
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#define NUMBER_OF_MOVIES 6
using namespace std;
int tokenize(string line);
int getMovieNum(char movie);
void resetPos(istream& flix);
class Matrix{
public:
int movieRate[NUMBER_OF_MOVIES];
};
int main(){
int distinctCount = 0;
int checker = -1;
int check = 0;
string line;
int personNum;
char movie;
int rating;
int movieNum;
ifstream flix("flix.txt");
ofstream flick("flix1.txt");
//identify distinct account numbers in file
while(getline(flix, line)){
check = tokenize(line);
if(check != checker)
distinctCount++;
checker = check;
check = 0;
}
//reset position in file
resetPos(flix);
//create objects in accordance with distinct numbers
Matrix* person = new Matrix[distinctCount];
for(int i = 0; i < distinctCount; i++){
for(int j = 0; j < NUMBER_OF_MOVIES; j++){
person[i].movieRate[j] = 0;
cout << i + 1 << ' ' << person[i].movieRate[j] << endl;
}
cout << "\n";
}
//reset position in file
resetPos(flix);
//get data from file and put into respective variables
while(getline(flix, line)){
flix >> personNum >> movie >> rating;
cout << personNum << ' ' << movie << ' ' << rating << endl;
//changes the char into an int
movieNum = getMovieNum(movie);
person[personNum].movieRate[movieNum] = rating;
}
//reset position in file
resetPos(flix);
//input ratings into movie array
for(int i = 0; i < distinctCount; i++){
for(int j = 0; j < NUMBER_OF_MOVIES; j++){
cout << i + 1 << ' ' << person[i].movieRate[j] << endl;
flick << i + 1 << ' ' << person[i].movieRate[j] << endl;
}
}
//write data to text file
//??
flick.close();
//free memory
delete[] person;
system("pause");
return 0;
}
int tokenize(string line){
string myText(line);
istringstream iss(myText);
string token;
getline(iss, token, ' ');
int strInt = atoi(token.c_str());
return strInt;
}
int getMovieNum(char movie){
int movieNum = 0;
switch(movie){
case 'A':
movieNum = 1;
break;
case 'B':
movieNum = 2;
break;
case 'C':
movieNum = 3;
break;
case 'D':
movieNum = 4;
break;
case 'E':
movieNum = 5;
break;
case 'F':
movieNum = 6;
break;
default:
movieNum = 0;
break;
}
return movieNum;
}
void resetPos(istream& flix){
flix.clear();
flix.seekg(0);
}
I also apologize in advance if there are noobish mistakes here.
I think the problem is somewhere in the while loop, that's where it keeps locking up. I spent hours on this and I can't figure out why it doesn't work. In the while loop, I'm trying to access every line of the file, snag the data from the line, take the movie char and turn it into an int, and then plug the data into the array within the object. When I did have it working, all the data was wrong too. Any input is highly appreciated. Thanks in advance.
You need to change a little in your program and i'l paste only the changed part.
Somewhere after you reset the person[].movieRate[] to zero,you have written this while loop
resetPos(flix);
int k = NUMBER_OF_MOVIES + 2; //this is the variable that i have declared
//get data from file and put into respective variables
while(k){ //do you see that instead of getline() i have used the variable k. i'l tell you why later
flix >> personNum >> movie >> rating;
//personNum = tokenize(line,1);
cout << personNum << ' ' << movie << ' ' << rating << endl;
//changes the char into an int
movieNum = getMovieNum(movie);
person[personNum - 1].movieRate[movieNum] = rating; //this is personNum-1 and NOT personNum the most common mistake while indexing array.
k--;
}
this code seems to work as your criteria.
the reason that i removed getline() is, if u call getline then the get pointer position will be incremented. so after this you call flix >> something... , this reads the data from the second line. your first line 1 A 5 is lost. this was the cause of the trouble. change it n let me know.
Okay, let me try to give at least some idea of a simple starting point:
#include <iostream>
#include <iterator>
#include <vector>
#include <fstream>
struct rater {
std::vector<int> ratings;
rater() : ratings(6) {}
friend std::istream &operator>>(std::istream &is, rater &r) {
char movie;
is >> movie;
return is >> r.ratings[movie-'A'];
}
friend std::ostream &operator<<(std::ostream &os, rater const &r) {
for (int i=0; i<r.ratings.size(); i++) {
os << char(i + 'A') << ":" << r.ratings[i] << "\t";
}
return os;
}
};
int main() {
std::ifstream in("flix.txt");
std::vector<rater> ratings(5);
int i;
while (in >> i)
in >> ratings[i-1];
i=1;
for (auto r : ratings)
std::cout << i++ << "-> " << r << "\n";
}
Here's a bit of a clean up. It uses std::map to keep track of the Person and Movie keys, which is more flexible as textual strings of any kind (sans whitespace) can be used. You've added a comment saying you specifically want to list movies people didn't rate in their outputs - that can be done by using a std::set and ensuring each movie name encountered is inserted, then using an iteration over the set to guide lookups in each person's ratings: left as an exercise.
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <map>
using namespace std;
typedef std::map<std::string, int> Movie_Ratings;
typedef std::map<std::string, Movie_Ratings> Persons_Movie_ratings;
int main()
{
if (!ifstream flix("flix.txt"))
{
std::cerr << "error opening input\n";
exit(1);
}
if (!ofstream flick("flix1.txt"))
{
std::cerr << "error opening output\n";
exit(1);
}
Persons_Movie_Ratings ratings;
std::string line;
while (getline(flix, line))
{
istringstream iss(line);
string person, movie;
int rating;
if (line >> person >> movie >> rating)
ratings[person][movie] = rating;
}
// input ratings into movie array
for (Persons_Movie_Ratings::const_iterator i = ratings.begin();
i != ratings.end(); ++i)
{
for (Movie_Ratings::const_iterator j = i->second.begin();
j != i->second.end(); ++j)
{
cout << i->first << ' ' << j->second << endl;
flick << i->first << ' ' << j->second << endl;
}
}
system("pause");
}