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);
}
Related
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(...);
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.
I have a program that is supposed to read an input file via string stream. The file contains student names, and test scores that I want to average. The file can contain any number of test scores for any number of students greater than 1 and less than 10.
If I am reading all the values in the file by string stream, how would I store each test score value as an integer where I can sum them? Here is the code I have so far, which I am not sure is even correct:
string fname, lname, line;
getline(cin, line);
istringstream sin;
sin.str(line);
sin >> fname >> lname;
Is this the right way to parse through values? At the top, I declared a struct 'student' like this:
struct student {
string first_name;
string last_name;
double avg_score;
} student1;
Thank you!
If each line varies in the number of scores I'd tend to read in complete lines and parse them, one after the other. Thereby you can rely on the >>-operator to return false once no more score could be read in a line. So I think you are on the right way. See the following code demonstrating how to deal with the return values of >>:
int main() {
ifstream f(DATAFILE);
if(f) {
string line;
while (getline(f,line)) {
string fname,lname;
istringstream ss(line);
if (ss >> fname >> lname) {
double sum = 0;
double value;
int count = 0;
while (ss >> value) {
sum += value;
count++;
}
cout << line << " gives average: " << sum/count << endl;
}
}
}
}
Storing the values in a struct is straight forward (and left up to you :-)). In case you face troubles please ask.
Hope it helps.
If you need to store multiple data you typically need a so-called container class. Container classes can store an arbitrary number of data of the same type and provide methods to manage the container elements.
The standard container class in C++ is std::vector, so, for example, in your case you may define a container by
#include <vector>
std::vector<student> allStudents;
To add the data of student1 you may do
allStudents.push_back(student1)
For anything else you better read a beginner's C++ text book, as working with container classes is a basic skill of a C++ programmer.
void ListaS::crearListaAleatoria(){
ifstream infile;
ifstream xfile;
infile.open("datosPrueba.txt");
xfile.open("datosPruebaNombres.txt");
int id;
char nombre[100];
int counter = 0;
//En caso de error
if (infile.fail()){
cout << "Error opening file" <<endl;
exit(1);
} if (xfile.fail()){
cout << "Error opening file" <<endl;
exit(1);
}
while(infile.eof() && xfile.eof()){
Persona* p = new Persona();
infile >> id;
xfile >> nombre;
p->setId(id);
p->setNombre(nombre);
agregar(p);
}
}
So I'm trying to build a linked list with two text files, one has numbers and the other has names, nonetheless, whenever I attempt to print the contents of this list, through another method I have somewhere else, it tells me I'm trying to access null values. The object Persona* is the place where I store the id and the name while agregar() is what creates the nodes to add to the list which is created elsewhere. Those things are not causing problems, its mainly those two values. I don't suppose there's some way to convert infile >> id to an int? Is there?
By the way, the condition of your while loop is wrong (should be while(!infile.eof() && !xfile.eof())). But in C++ you usually do these things in a different way:
while(infile >> id && xfile >> nombre){
Persona* p = new Persona();
p->setId(id);
p->setNombre(nombre);
agregar(p);
}
This way you read the values from the files and check the ifstream state at the same time...and you avoid problems with last lines.
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class PERSON{
string name, surname;
public:
void set_name(string aname, string asurname){
name = aname;
surname = asurname;
};
void read(){
}
void output(){
cout << name << " " << surname << "\n";
}
void save(PERSON *x){
ofstream file("test.bin", ios::out | ios::app | ios::binary);
if(!file.is_open()){
cout << "ERROR\n";
}else{
file.write((char*)x, sizeof(*x));
file.close();
}
}
};
/*
*
*
*
*/
int main(int argc, char** argv) {
PERSON * person1 = new PERSON;
PERSON * person2 = new PERSON;
person1->set_name("Amon", "Raa");
person1->save(oseba1);
ifstream file2("test.bin", ios::in | ios::binary);
if(!file2.is_open()){
cout << "Error\n";
return 0;
}
while(!file2.eof()){
file2.read((char *)person2, sizeof(*person2));
person2->output();
}
file2.close();
return 0;
}
This is my code...what I am doing wrong?
What I am trying to do is to save each time a class to the end of the binary file and then read all entries...
but each time I run the program I get printed only the last entered name
so run it first time
the file is written correctly and the output is OK
then I change the name to something else, lets say John Doe, I get the output of 2times John Doe
Please help... I am a complete beginner ;(
Serialization of classes is for example included in the boost package.
http://www.boost.org/doc/libs/1_53_0/libs/serialization/doc/index.html
I do not think you actually want to implement these kind of functionality yourself.
You can't simply write out the binary image of a class in C++, especially one that contains pointers and non-POD members. Once you have indirections in your data, simply writing out a memory image doesn't work, both because just writing out the memory image of the class doesn't include the pointed-to data and because in order for this to work you'd have to load all the data including the pointed-to data into the exact same memory location that they were in when you saved them. That's not easily possible (to put it mildly).
You have two options, one manual, one using a third party library:
1) You write and read each member out separately with a bunch of bookkeeping information. That should work in you case as all you really have to load and save is the contents of the two strings and their respective lengths
2) The other option - especially when the data structure is more complex than the one you are using - is to use something like boost::serialization to do the grunt work for you.
You have to use pointer array of class PERSON. Then read from the binary file and populate array of persons.
ifstream input("PERSON.std",ios::binary);
input.seekg(0, ios::end);
int count = input.tellg() / sizeof(PERSON);
PERSON *persons = new PERSON[count];
input.seekg(0, ios::beg);
input.read((char*) persons, sizeof(PERSON)*count);
cout << count << endl;
for (int j = 0; j < count; j++)
{
cout << count << endl;
cout <<persons[j].output() << "\n";
}
cout << '\n';
input.close();