I have a players.txt file where I need to read from and put my readings in a constructor and then I will make a list of these TennisPlayer objects. And I am stuck with how to do it. Well, the first thing is I read from file word by word or line by line but couldn't manage how to put my readings into constructor.
My constructor has five inputs:
TennisPlayer(string firstName, tring lastName, int ranking, int totalPoints, string country)
And part of my players.txt file is here:
Novak Djokovic 16790 Serbia
Andy Murray 8945 Great Britain
And secondly, how can take "Great Britain" as one string?
I am really new in C++ and in a desperate position. Thank you all for your help.
Create a stream from a file and then pass it to a class like this. It will iterate over the file, store the values in a vector and then initialise a vector of those objects in the constructor. To read the country, we keep reading in words until we find the end of the line. Therefore, I am working under the assumption that each tennis player is specified in a single line.
Note that there is no error checking here, you may wish to add that
class TennisPlayersList
{
public:
struct TennisPlayer
{
std::string first_name;
std::string second_name;
int ranking;
int total_points;
std::string country;
};
TennisPlayersList(std::istream& filestream)
:players(ReadPlayersFromFile(filestream))
{}
private:
std::vector<TennisPlayer> players;
static std::vector<TennisPlayer> ReadPlayersFromFile(std::istream& filestream)
{
std::vector<TennisPlayer> p;
while (filestream.eof() == false)
{
p.push_back(ReadPlayer(filestream));
}
return p;
}
static TennisPlayer ReadPlayer(std::istream& filestream)
{
using namespace std;
TennisPlayer player;
std::string line;
getline(filestream, line);
std::stringstream ss(line);
ss >> player.first_name;
ss >> player.second_name;
ss >> player.ranking;
ss >> player.total_points;
// country could be several words, keep appending until we have finished
while (ss.good())
{
std::string country_part;
ss >> country_part;
if (player.country.empty())
player.country += country_part;
else
player.country += " " + country_part;
}
return player;
}
};
call with something like this
std::fstream players;
players.open("tennis_players.txt", std::ios::in);
TennisPlayersLists list(players);
players.close();
Related
In my project, I have two structs:
struct spell {
string name;
string effect;
};
struct spellbook {
string title;
string author;
int pages;
struct spell* s;
}
The spellbook and spell objects are arrays in this program. I'm reading information from a file and putting it into these two structs. An example of the file is:
2 //Number of spellbooks
Dark_Secrets Grady 357 //Title author pages
Evil_Force Dark //Spell name and effect
Grimoire Thompson 1967
Heavenly_Glow Light
Water_Ray Water
So the spellbook lines end with an integer (page number), while the spell lines end with a letter (The number at the top of the file is taken care of by another method). My plan for distinguishing between them was to check the last char in the line, and seeing if it was a digit. If it was, then I would add it to the spellbook struct. If not, then I would add it to the spell struct. As far as implementing this, I'm not sure how. Here is my add to spellbook struct method: (I have to have 2 different methods for this assignment)
void get_spellbook_data(spellbook* sb, int n, ifstream &file) //Int n is the number of spellbooks {
char end_line;
int i = 0;
//Jump to end of current line. I tried using file,seekg(-1, ios_base::end) but it got me to the end of the file, not the line
file.get(end_line); //Get last character in the line
//Jump back to the beginning of the current line
if (isdigit(end_line)) { //If current line is spellbook
file >> sb[i].title >> sb[i].author >> sb[i].pages;
i++;
}
else {//If its a spell line
//Skip this line and advance to the next one
}
}
That's my current idea so far. The method to add spells would basically be the same as this except checking if the current line does not end with a digit. I feel like there is a better method to get the information, but I'm not exactly sure of how to implement it.
Typical way to read a file line by line:
std::string line;
while (true) {
if (!std::getline(ifs, line)) {
// either eof or error, check ifs.eof()
// need to handle this somehow, leaving this up to you
}
// Now you have the whole text line in the line.
if (!line.empty()) break;
// the line was empty for some reason, continue to next line, until
// you get non-empty line, eof or error
}
// Now you have non-empty line. Check that the last char is digit...
if (std::isdigit(line.back()) {
// the last character is digit
}
// but I'd split line into space separated tokens and check if there is 2 or 3 tokens.
If I were you, I'd read a whole line in at a time, and then determine if I'm looking at a book or a spell line. So the code may look like:
struct spell {
std::string name;
std::string effect;
spell(std::string str) {
std::istringstream strm(str);
strm >> name >> effect;
}
};
struct spellbook {
std::string title;
std::string author;
int pages;
std::vector<spell> spells; // vector for nicer code and a cleaner name
spellbook(std::string str) {
std::istringstream strm(str);
strm >> title >> author >> pages;
}
};
bool is_book(std::string str) {
return isdigit(str.back());
}
int main() {
std::vector<spellbook> books;
ifstream fhand("Some/File/Location.txt");
std::string line;
while(std::getline(fhand, line)) {
if(is_book(line)) {
books.push_back(spellbook(line));
} else {
books.back().spells.push_back(spell(line));
}
}
}
Here's a live example (using stdin instead of a file): https://ideone.com/s9qk7Y
As a noob C++ project, I am building a CLI game with a game loop that relies upon user input. For the purpose of testing, I would like to be able to pass a file name as a command line argument to the program, which will be treated "like standard input" until it is read through.
Consequently, I need a way to encapsulate an object that represents a file's contents and then std::cin once the file has been read through - basically prepending a file's contents to standard input. At least, this seems like it would be very nice, so that I can avoid having something like
std::istringstream retrieve_input(std::ifstream &file) {
std::string line;
if (std::getline(file, line))
return std::istringstream{line};
std::getline(std::cin, line);
return std::istringstream{line};
}
Is replacing this with some kind of custom class a good approach? Or can I mess with std::cin somehow to prepend my file contents to it?
To clarify : My game loop might have say, 20 iterations. If I pass a file with five lines, I want to use those five lines for the first five iterations of my game loop, and then drop back to standard input for the remaining fifteen. I understand how to do this with a bunch of conditions, but I think there must be a way to nicely have this sort of behavior in a class. My problem is - what should it inherit from? std::streambuf? Is this a good idea in principle?
My (probably bad attempt)
class InputGrabber {
public:
virtual std::istringstream retrieve_line() = 0;
virtual ~InputGrabber() {}
};
class BaseInputGrabber : public InputGrabber {
public:
BaseInputGrabber(std::istream &_in): in{_in} {}
std::istringstream retrieve_line() override {
std::string line;
std::getline(in, line);
return std::istringstream{line};
}
private:
std::istream ∈
};
class VarInputGrabber : public InputGrabber {
public:
VarInputGrabber(std::istream &_in, const std::string &f_name) :
in{_in}, init{std::ifstream(f_name)} {}
std::istringstream retrieve_line() override {
std::string line;
if (std::getline(init, line)) {
return std::istringstream{line};
}
std::getline(in, line);
return std::istringstream{line};
}
private:
std::istream ∈
std::ifstream init;
};
Would the below work? You open the file, redirect std::cin to read from the file. Use std::getline on std::cin so that it reads from the file stream. Some time before the program ends or if the file is finished being read, restore std::cin back to its original state.
#include <iostream>
#include <fstream>
int main(int argc, const char * argv[]) {
std::fstream file("/users/Brandon/Desktop/testingInput.txt", std::ios::in);
//Redirect cin to a file.
std::streambuf* cinBuffer = std::cin.rdbuf();
std::cin.rdbuf(file.rdbuf());
//Read line from std::cin (which points to the file) with std::getline.
std::string line;
std::getline(std::cin, line);
std::cout<<"INPUT LINE: "<<line<<std::endl;
//Redirect cin to standard input.
std::cin.rdbuf(cinBuffer);
//Read some line from the user's input..
std::getline(std::cin, line);
std::cout<<"USER INPUT LINE: "<<line<<std::endl;
std::cin.get();
return 0;
}
You could always redirect from file via argv. Treat the whole thing as std::cin from the beginning.
Is the following example that what you are looking for?
std::string readNextLine(std::istream& is)
{
std::string line;
if(!std::getline(is, line))
std::getline(std::cin, line);
return line;
}
int main()
{
std::stringstream ss ("a\nb\nc\n");
for(int i = 0; i < 5; ++i)
{
auto line = readNextLine(ss);
std::cout << "got: " << line << std::endl;
}
return 0;
}
The stringstream can be replaced with fstream or any other type that implements std::istream.
You can test it here http://cpp.sh/5iv65
If I have a file containing 10 rows, where each row contain information about the following object,
class student
{
public:
string name;
int age;
int rollnum;
int year;
string father;
string mother;
string category;
string region;
char sex;
string branch;
int semester;
};
How can I read all the 10 objects information from a file? ( I am guessing I will have to take an array of 10 objects for this )
istream& operator >>(istream& in, student& val) {
return in >> name >> age >> rollnum >> year; // ...
}
Then you can do this:
for (string line; getline(infile, line); ) {
istringstream stream(line);
student person;
stream >> person;
}
Now person is being populated once per line. I do it this way rather than directly streaming from the file because this way is safer: if a line has the wrong number of tokens it won't misunderstand what the columns are, whereas without getline() you might naively parse 10 tokens of an 11 token line, then think that the 11th token on the line is the first token of the next record. Just a typical mistake that happens in beginner C++ text parsing code.
I'm new to C++ and I'm having a little trouble when it comes to reading lines of data from a text file. Let's say I have an unknown number of lines in a text file, with each line in the same format: int string double . The only thing that will be definite is a space will separate each piece of data on a given line. I'm using an array of structs to store the data. The code below works great except that it skips a line of input after each loop. I've tried inserting various ignore() statements and still can't get it to read each line, only every other line. If I rewrite some of the getline statements at the end, then the wrong data starts getting stored for the variables after the first loop.
Text file might look like this:
18 JIMMY 71.5
32 TOM 68.25
27 SARAH 61.4
//code
struct PersonInfo
{
int age;
string name;
double height;
};
//..... fstream inputFile; string input;
PersonInfo *people;
people = new PersonInfo[50];
int ix = 0;
getline(inputFile, input, ' ');
while(inputFile)
{
people[ix].age = atoi(input.c_str());
getline(inputFile, input, ' ');
people[ix].name = input;
getline(inputFile, input, ' ');
people[ix].height = atof(input.c_str());
ix++;
getline(inputFile, input, '\n');
getline(inputFile, input, ' ');
}
I'm sure there are more advanced ways to do this, but like I said I'm pretty new to C++ so if there are just some slight modifications to the code above, that would be great. Thanks!
You can do the file reading as follows:
int ix = 0;
int age = 0;
string name ="";
double height = 0.0;
ifstream inputFile.open(input.c_str()); //input is input file name
while (inputFile>> age >> name >> height)
{
PersonInfo p ={age, name, height};
people[ix++] = p;
}
You've made this whole code ridiculously complicated.
struct PersonInfo
{
int age;
string name;
double height;
};
std::vector<PersonInfo> people;
PersonInfo newPerson;
while(inputFile >> newPerson.age >> newPerson.name >> newPerson.height)
people.push_back(std::move(newPerson));
Your problem is because first your read each bit of data one at a time from the file, then a whole line from teh file, then each bit of data one at a time from the file again. Maybe what you intended was more like this?
std::string fullline;
while(std::getline(inputFile, fullline)) {
std::stringstream linestream(fullline);
std::getline(linestream, datawhatever);
....
}
By the way, more idiomatic code might look like more like this:
std::istream& operator>>(std::istream& inputFile, PersonInfo& newPerson)
{return inputFile >> newPerson.age >> newPerson.name >> newPerson.height;}
{ //inside a function
std::ifstream inputFile("filename.txt");
typedef std::istream_iterator<PersonInfo> iit;
std::vector<PersonInfo> people{iit(inputFile), iit()}; //read in
}
Proof it works here
I am having some trouble with my lab assignment for my CMPT class...
I am trying to read a text file that has two words and a string of numbers per line, and the file can be as long as anyone makes it.
An example is
Xiao Wang 135798642
Lucie Chan 122344566
Rich Morlan 123456789
Amir Khan 975312468
Pierre Guertin 533665789
Marie Tye 987654321
I have to make each line a separate "student", so I was thinking of using struct to do so, but I don't know how to do that as I need the first, last, and ID number to be separate.
struct Student{
string firstName;
string secondName;
string idNumber;
};
All of the tries done to read in each word separately have failed (ended up reading the whole line instead) and I am getting mildly frustrated.
With the help from #Sylence I have managed to read in each line separately. I am still confused with how to split the lines by the whitespace though. Is there a split function in ifstream?
Sylence, is 'parts' going to be an array? I saw you had indexes in []'s.
What exactly does the students.add( stud ) do?
My code so far is:
int getFileInfo()
{
Student stdnt;
ifstream stdntFile;
string fileName;
char buffer[256];
cout<<"Please enter the filename of the file";
cin>>filename;
stdntFile.open(fileName.c_str());
while(!stdFile.eof())
{
stdFile.getLine(buffer,100);
}
return 0;
}
This is my modified and final version of getFileInfo(), thank you Shahbaz, for the easy and quick way to read in the data.
void getFileInfo()
{
int failed=0;
ifstream fin;
string fileName;
vector<Student> students; // A place to store the list of students
Student s; // A place to store data of one student
cout<<"Please enter the filename of the student grades (ex. filename_1.txt)."<<endl;
do{
if(failed>=1)
cout<<"Please enter a correct filename."<<endl;
cin>>fileName;
fin.open(fileName.c_str());// Open the file
failed++;
}while(!fin.good());
while (fin >> s.firstName >> s.lastName >> s.stdNumber)
students.push_back(s);
fin.close();
cout<<students.max_size()<<endl<< students.size()<<endl<<students.capacity()<<endl;
return;
}
What I am confused about now is how to access the data that was inputted! I know it was put into a vector, but How to I go about accessing the individual spaces in the vector, and how exactly is the inputted data stored in the vector? If I try to cout a spot of the vector, I get an error because Visual Studio doesn't know what to output I guess..
The other answers are good, but they look a bit complicated. You can do it simply by:
vector<Student> students; // A place to store the list of students
Student s; // A place to store data of one student
ifstream fin("filename"); // Open the file
while (fin >> s.firstName >> s.secondName >> s.idNumber)
students.push_back(s);
Note that if istream fails, such as when the file finishes, the istream object (fin) will evaluate to false. Therefore while (fin >> ....) will stop when the file finishes.
P.S. Don't forget to check if the file is opened or not.
Define a stream reader for student:
std::istream& operator>>(std::istream& stream, Student& data)
{
std::string line;
std::getline(stream, line);
std::stringstream linestream(line);
linestream >> data.firstName >> data.secondName >> data.idNumber;
return stream;
}
Now you should be able to stream objects from any stream, including a file:
int main()
{
std::ifstream file("data");
Student student1;
file >> student1; // Read 1 student;
// Or Copy a file of students into a vector
std::vector<Student> studentVector;
std::copy(std::istream_iterator<Student>(file),
std::istream_iterator<Student>(),
std::back_inserter(studentVector)
);
}
Simply read a whole line and then split the string at the spaces and assign the values to an object of the struct.
pseudo code:
while( !eof )
line = readline()
parts = line.split( ' ' )
Student stud = new Student()
stud.firstName = parts[0]
stud.secondName = parts[1]
stud.idNumber = parts[2]
students.add( stud )
end while