I have class player that contains some attributes.
I m filling a file with player's data but i want to assign a new id number every time the function is called in a way that the new player's id will be increased every time.
So i made this while loop to read number of players and update the player's new id but this doesn't work and nothing is written in the file.
void Player::create_player()
{
fstream file;
Player plyr; string passwordt;
int aget = 0;
long int id_nmbert = 0;
string emailt, ingame_namet, full_namet,yes;
file.open("player_database.dat", ios::binary | ios::app);
if (!file.is_open())
throw exception();
while (file >> yes) ///
id_nmbert++; ///
cout << "insert player's full name" << endl;
cin >> full_namet;
plyr.full_name = full_namet;
cout << "insert player's age" << endl;
cin >> aget;
plyr.age = aget;
cout << "insert player's email" << endl;
cin >> emailt;
plyr.email = emailt;
cout << "insert player's password" << endl;
cin >> password;
plyr.password = passwordt;
cout << "insert player's ingame name" << endl;
cin >> ingame_namet;
plyr.ingame_name = ingame_namet;
plyr.id_nmber = id_nmbert;
file.write((char*)&plyr, sizeof(Player));
file.close();
}
Tried This but it's even worse.
while (!file.eof())
id_numbert;
There are similar questions but not in c++ :).
I'd recommend storing the players in a text file that you parse instead of storing them as binary data.
This has a lot of advantages.
The biggest difficulty with your approach is that you need to design a binary format for your player object.
Something like
id+N1+'fullname'+N2+'email'+N3+'ingame-name'
4 bytes id, N1 number of characters in fullname(4 bytes),
the characters in fullname as indicated by N1
same for N2 + email and so on.
This requires fairly complicated logic for reading and writing a player.
On the other hand you could have a text file looking like
1
Yassin Mrabet
20
player#gamers.net
playerino1336
2
Captain Giraffe
55
player1#gamers.net
playerino1337
This is a lot easier to write code for.
A new player every fifth getline.
A sample readPlayer member could look like (should probably be static, not super important right now)
Plkayer Player::readPlayer(istream& in){
std::string str_id;
std::getline(in, str_id);
std::string fullname;
std::getline(in, fullname);
std::string str_age;
std::getline(in, str_age);
std::string email;
std::getline(in, email);
std::string player_name;
std::getline(in, player_name);
int age = std::stoi(str_age);
int id = std::stoi(str_id);
if(!in) // read was unsuccessful.
handle_bad_read();
return Player(id, fullname, age, email, player_name);
}
The return statement of course requires a constructor that accepts exactly those arguments, but that constructor will be useful in many other situations too.
The question isn't much specific as for what doesn't work, however, for keeping track of the ID I suggest using a static variable at the top of the function like this:
static int playerid = 0;
The static keyword means that it will only set the variable to zero once, and it will not delete the data after the end of the function. Which means you just save the 'playerid' into the file, then increase it by 1. The next time the function will be called, playerid will be larger by 1.
Edit: while (!file.eof()) doesn't work because you're using ios::app, it already starts at file.eof(), so the loop shouldn't do anything.
Related
Here my code. I want to declare dynamic array and next thing is to be able to read file and add elements in array. it is compiling, but I do not see result. Your suggestion is welcome.
int main(int argc, char** agrv)
{
if(argc < 2 || argc > 2)
{
cerr << "You have to provide a file" << endl;
return -1;
}
string myFile = agrv[1];
ifstream file(myFile.c_str());
if (!file)
{
cerr << "Error, file do not exist" << endl;
return -1;
}
Student students;
string number_student, name_student, surname_student, code_student;
double number_student;
// Declare an new array
DynamicArray<Student>students;
while(file >>number_student >> name_student >> surname_student >> code_student)
{
students.add(student);
}
for(int i = 0; i < students.size(); i++)
cout << students[i] << endl;
To provide solution to my project
First of all, you used the same name students for 2 variables in the code.
Student students
and
DynamicArray<Students> students;
I think you probably wanted to name one of them student and the other students but made a typo mistake that lead to 2 students? When you're calling students.add(student), it is obvious there is no variable named student to add to the array.
Then again, you have both string number_student and double number_student the same variable name. I have no idea why would you need 2 different variables for this one because your code didn't provide the background information about it, so at the moment I will eliminate the double number_student one for simplicity.
Second, there is some logic hole in your code. You used these strings number_student, name_student, surname_student and code_student to store the variable, but I don't see you use them again. My guess is that you're having a struct or class name Student and you want to store those variables into Student. Since you didn't provide your struct/class Student, I cannot help you fix it, but I can write you an example of it.
struct Student {
string number_student;
string name_student;
string surname_student;
string code_student;
}
To add a new student each time, you must call the Student student inside the while loop to create a new struct.
while(file >> number_student >> name_student >> surname_student >> code_student)
{
Student student = { number_student, name_student, surname_student, code_student};
students.add(student);
}
I am making a program where I input the details of many employees and store it in a text file. How can I make a function to search the entire text file and display the only details of that employee and not anyone else's?
The details is always being inputted in append mode. I can't use eof() as it will display the entire document.
This is a school project and we have only studied cin and cout and not std::, hence I am using using namespace std;
EDIT: ADDED SAMPLE TEXT FILE
First name:Test
Last name: asdfas
Employee no: 12
(etc.)
Local Contact: 12323
***********************************************
First name:Test2
Last name: asd
Employee no: 23432
(etc.)
Local Contact: 234324
***********************************************
void hr::empdetails()
{
//declaring all datamembers
char firstname [30], lastname [30], dept[30]; //etc.
ofstream outfile;
outfile.open ("example.txt",ios::app);
//inputting all details
//writing details into text file...
outfile<<"First name:";
outfile<<firstname;
//...................
outfile<<"\nLocal Contact: ";
outfile<<localcon;
outfile<<"\n\n*************************************************";//indicating end of employee's details
}
void hr::searchname()
{
//what should i write here to search for a name and display all of its details
}
In most cases, the method is to read in all the fields in a record and only use the fields that you need. Reading the extra fields will not take any extra time versus executing code to skip over them.
Also, prefer arrays (std::vector) of structures to parallel arrays:
struct Employee_Record
{
std::string first_name;
std::string last_name;
int id;
//...
};
std::vector<Employee_Record> database;
Employee_Record array[32];
You can make the input simpler by overloading operator>> for the structure:
struct Employee_Record
{
//...
friend istream& operator>>(istream& input, Employee_Record& er);
};
istream& operator>>(istream& input, Employee_Record& er)
{
getline(input, er.first_name);
getline(input, er.last_name);
//...
return input;
}
You input code would look something like this:
std::vector<Employee_Record> database;
Employee_Record er;
while (data_file >> er)
{
database.push_back(er);
}
A common technique is to read in all the data, then process the data (such as searching).
int main()
{
ifstream fin("look.txt");. // Here you have to provide file name
string line; // takes a line at a time.
int person = 1; // this increments person
while (getline(fin, line)) // here we are reading data line by line till eof
{
if (line == "***********************************************") // this is point where we increment the person variable by one ( to change person )
person++;
int ind = line.find_last_of(':'); // here we are finding ':' character to get fields name like First Name , Last Name ,etc..
string cc = line.substr(0, ind); // here we get value of the fields ex:- First Name :-Sha11 ( here we fetch Sha11 .. you use this way to compare empolyees various value ,in your desired way.. )
if (cc == "First name" || cc == "Last name" || cc == "Local Contact") ( It is looking only for some desired fields , but you might change this according to you. )
{
if (ind != string::npos)
{
int diff = line.size() - ind - 1;
string pa = line.substr(ind + 1, diff);
cout << person << " : " << cc << " : " << pa << endl; // here cc stores the field's name and pa stores the field's value. here i used substr() and find() to get desired results from the string (for more details about these function look at these urls "www.cplusplus.com/reference/string/string/find/" , "http://www.cplusplus.com/reference/string/string/substr/")..
}
}
}
return 0;
}
This commented explanation might help you...!
This might solve your problem....
As part of my assignment I need to open a file and then read the information into 3 arrays. These information are separated into 3 different columns first one is, Country Code name(is string), second is the population(is int) and the third is the full name of the country. Here is the example of few lines of the file:
AU 20090437 Australia
BR 186112794 Brazil
BU 7262675 Bulgaria
CA 32805041 Canada
CN 1306313812 China
DO 8950034 Dominican Republic
So far I have:
void readCntrData(string [], int [], string[], int &size, const int maxSize);
int main()
{
const int COUNTRIES = 200;
// maximum size of arrays
int size = 0;
string cntrCodes[COUNTRIES];
int cntrPopulation[COUNTRIES];
string cntrNames[COUNTRIES];
string inputFileName = "countries.txt";
ifstream inputFile;
inputFile.open(inputFileName.c_str());
if (inputFile.fail())
{
cout << "\n\tPlease check the name of the input file and \n\ttry again later!\n";
exit(EXIT_FAILURE);
}
int index = 0;
while (index < COUNTRIES && inputFile >> cntrCodes[index] >> cntrPopulation[index] >> cntrNames[index] ) {
index++;
}
size = index;
if (size == COUNTRIES && !inputFile.eof()){
cout << "\n\tThe input file \"" << inputFileName <<
"\"is too big: \n\tit has more than " << COUNTRIES << " items!\n";
exit(EXIT_FAILURE);
}
inputFile.close();
}
The issue here few countries have two part names, and my code breaks where the name of country has two parts. I don't know how to ignore the space there and read the whole name.
I appreciate any feedback.
Just cleaning the code up a bit...
the main problem you ask about in your question has already been addressed by user4581301 in comments: summarily, using getline to read the country code will cope with having one or more whitespace-separated words
since C++11, we've been able to use the ifstream constructor to open a filename passed in a std::string, and it's often convenient to construct the ifstream inside an if/else construct that then handles the successful-open and failed-attempt-to-open cases
there's no need to explicitly close ifstreams that are going out of scope anyway
it's good to send error messages to std::cerr, so if someone running your program does something like "theprogram > theprogram.output" they'll still see the error messages in their terminal
no real need to have separate index and size variables, and something like num_countries is a better, more meaningful name
checking for EOF is a bit of a dark art - I wouldn't recommend using inputFile.eof() the way you did because if the last country read was followed by an empty line in the file, you wouldn't have hit end-of-file even though it's "logically" empty thereafter; it's too easy for someone not to notice empty lines at the end of a file when they're working in a editor to create/update the file; checking if there's another non-whitespace character is a more robust approach
the common size for int these days is 32 bits, which can handle numbers up to a couple billion (when signed, or a bit over 4 billion for unsigned int); China's population's a bit too close to that for comfort - if you want to write code in a way that should still work 20 or 50 years hence, it's good to think about whether the types will still be large enough to store the values; int64_t from <cstdint> is a better choice.
Revised code:
int main()
{
const int COUNTRIES = 200;
string cntrCodes[COUNTRIES];
int64_t cntrPopulation[COUNTRIES];
string cntrNames[COUNTRIES];
string inputFileName = "countries.txt";
if (std::ifstream inputFile{inputFileName})
{
int num_countries = 0;
while (num_countries < COUNTRIES &&
inputFile >> cntrCodes[num_countries] >> cntrPopulation[num_countries] &&
getline(std::cin, cntrNames[num_countries]))
++num_countries;
// will see if we can read another character (remember >> skips
// whitespace by default), rather than check inputFile.eof()
// which may not be true if there're any extra empty lines after
// the data...
char character;
if (num_countries == COUNTRIES && inputFile >> character)
{
std::cerr << "\n\tThe input file \"" << inputFileName <<
"\"is too big: \n\tit has more than " << COUNTRIES << " items!\n";
exit(EXIT_FAILURE);
}
// any extra code to actually use the country data goes here...
}
else
{
std::cerr << "\n\tPlease check the name of the input file and \n\ttry again later!\n";
exit(EXIT_FAILURE);
}
}
For the last input of each line, you can use getline which reads everything until it meets an Enter key.
You can try this:
while (index < COUNTRIES && inputFile >> cntrCodes[index] >> cntrPopulation[index] ) {
getline (inputFile, cntrNames[index]);
index++;
}
I have a text file in this format:
Petroleum Engineering 94600 175500
Marine Engineering 73900 123200
Economics and Mathematics 60000 122900
Geophysics 54100 122200
Cognitive Science 54000 121900
What I have is course name, average early career pay, and mid career pay, all separated by tabs. The course names with multiple words are separated by spaces.
I want to read the course name and put it in one variable, the first pay in a second variable, and the third pay in a third variable.
int main(){
ifstream in;
in.open("Salaries.txt");
string course;
int mid;
int avg;
if (in.fail()){
cout<< "failed to open file";
exit(1);
}
while(!in.eof()){
in >> course >> avg >> mid;
cout << course<<endl;
}
in.close();
}
When I compile this code, it outputs nothing, and the program does not exit or terminate.
Someone in comments pointed out that using eof() is not recommended, so I tried thus instead:
while(in >> course >> sm >> lg){
cout << course << endl;
}
The process exits without outputting anything to the screen. I tried it on a input file that looks something like this:
NCORES 1
NEW 0
CORE 100
INPUT 5000
CORE 20
And it takes the string and puts it into one variable, and takes the number and puts it into another variable, and prints the correct output. So the problem is the white space between the words in the cours name in the original file, and I don't know how to account for that.
Although your code does have other problems, the problem you're running into is from the fact that operator>> for a string stops reading at the first white space it encounters. That means on the first line, it reads Petroleum into course, then tries to read Engineering into avg. Since avg is a number, that doesn't work, so the conversion fails. From there, all further attempts at reading from the stream fail.
To fix that, you probably want to use std::getline to read the course name. It allows you to specify the character that will end the string it reads. In this case, you apparently want to pass a tab character ('\t') for that parameter.
If I were doing it, I'd probably put the three items into a struct, and overload operator>> for that struct:
struct course {
std::string name;
long early_pay;
long mid_pay;
friend std::istream &operator>>(std::istream &is, course &c) {
if (std::getline(is, c.name, '\t')) {
std::string temp;
std::getline(is, temp, '\t');
c.early_pay = std::stol(temp);
std::getline(is, temp);
c.mid_pay = std::stol(temp);
}
return is;
}
};
Then reading the data and printing out the course names would look something like this:
int main() {
std::istringstream input(
R"(Petroleum Engineering 94600 175500
Marine Engineering 73900 123200
Economics and Mathematics 60000 122900
Geophysics 54100 122200
Cognitive Science 54000 121900 )");
course c;
while (input >> c)
std::cout << c.name << '\n';
}
Use std::getline() to read lines from the file, and use std::istringstream to parse each line. You can then use std::getline() to read tab-delimited strings from each line. For example:
int main() {
ifstream in("Salaries.txt");
if (!in.is_open()) {
cout<< "failed to open file";
exit(1);
}
string line course;
int mid, avg;
while (getline(in, line)) {
istringstream iss(line);
getline(iss, course, '\t');
iss >> avg >> mid;
cout << course << endl;
}
return 0;
}
I have the following block of code that i am using to read a text file of the following format:
firstname lastname id mark
firstname lastname id mark
Following is the block of code.
void DBManager::ReadFile(void){
fstream myfile; /*fstream object that will be used for file input and output operations*/
char* fn; /*pointer to the storage which will hold firstname*/
char* ln; /*pointer to the storage which will hold lastname*/
int id; /*integer var to hold the id*/
float mark; /*float var to hold the mark*/
/*read in the filename*/
g_FileName = new char[1024]; /*allocate memory on the heap to store filename*/
cout << "Please enter the filename:";
cin >> g_FileName;
/*open file*/
myfile.open(g_FileName, ios::in | ios::out);
if(myfile.is_open()){ /*check if the file opening is successful*/
cout << "File reading successful !\n";
/*read information from the file into temporary variables before passing them onto the heap*/
while (!myfile.eof()) {
fn=(char*) new char[1024];
ln=(char*) new char[1024];
myfile >> fn >> ln >> id >> mark;
cout << fn << " " << ln << " " << id << " " << mark << " " << endl;
}
myfile.close();
}
else{ /*else print error and return*/
perror("");
return;
}
}
The above block of code works ! :)
But I am surprised as to how myfile knows it is supposed to hold one line at a time and how its being smart enough about setting the four variables.
I am new to C++ , and hence this might be covered in some sort of documentation. But i would be happy to have some insight from you'll or a link to somewhere i can understand fstream objects better.
In C++, std::fstream is a type of stream which works specifically for files. When reading from a file, the interface for std::fstream is almost identical to std::cin. Input streams are programmed to read the next word or number when asked with the >> operator. They know where words and numbers are because they are separated by white space. In the default locale, spaces, tabs and newlines are considered to be white space. You can change the locale to include other characters, like commas, and have those be skipped while reading from a file. Basically, when reading with input streams, newlines and spaces are treated the same.
Some nice explanation for learning about streams is here: http://www.cprogramming.com/tutorial/c++-iostreams.html
I'm not sure what the question is. However, the code has several problems:
You should always check input after having tried to read.
Testing for eof() to determine if there is more to read doesn't work.
You have a memory leak, allocating memory in every iterator.
Reading without a constraint into a char array is unsafe, i.e., it is prone to buffer overrides (one of the major attack vectors).
You want to use a loop looking something like this:
std::string fn, ln;
while (myfile >> fn >> ln >> id >> mark) {
...
}