I want to overload the operator >>, so I can read some data from a file to save it in my class. My problem is that I don't know how to read one single word. Does a function like get() or getline() exist for this purpose?
For example, I have this class:
class Person{
private:
char * name;
int id;
public:
....
I have this file with some info:
James 23994
Anne 23030
Mary 300392
And what I want is to read the name and the id of these people to save them in my class.
Things are easier if you use std::string instead of a bare pointer that requires you to allocate memory, keep track of the size (or rely on null-termination), etc...
struct Person {
std::string name;
int id;
};
Now you can use the already existing operator<< for std::string and int :
std::istream& operator>>(std::istream& in, Person& p) {
in >> p.name >> p.id;
return in;
}
Note that operator>> does read input until it finds a ' ' by default, hence to "read a word" you dont have to do anything extra.
If you insist on having private fields, you should declare the operator as friend of the class.
Related
I am doing a project in my class that requires the program to stores teams into a vector. So I would have something like this:
string bballteam;
vector<string> teamVector;
and then I want the user to input team names. So I can just prompt
cin >> bballTeam;
teamVector.push_back(bballTeam);
to store the user input into the vector. However, I would like for the user input to be an array so that I can store players (like a roster). What are some ways I can implement this?
I acknowledge that you can't have arrays in a vector.
What you need to know is the following:
If you have many different attributes for one Object, then a struct or class must be used. If you have for example a "Player", then you would put the attributes of "Player" in a struct. Like this:
struct Player {
std::string firstName{};
std::string name {};
unsigned int age{};
double score{};
};
Here you have one Type with many attributes or properties.
If you need many "Objects" of same Type, then you would store those in an "array" type, like a C-Style array, or an std::array, or, if you need some variable size, a std::vector.
So, again
Many attributes of different Type will be stored in a struct
Many obejects with the same Type will be stored in n array.
Of course all these structures or arrays can be nested. You may have an array of struct or a struct containing arrays.
This depends on how you want to abstract the reality. It is about "relations".
For the input: Objects of the same Type are entered in "loops", e.g. a for or a while loop.
Object attributes of different Type will be entered one after the other.
If you have a struct or class and want to enter all its attributes with a simple extraction (>>) operation, then you will observe that the existing extract operator >> does not know, how to work with your Type. That, because you defined a new Type, for example "Player". So we need to tell the compiler how to "Get" the data. And for that, we need to define the input functionality for the new Type. This is done with so called operator overloading. Sorry, you need to read about that. But, it is simple for input and for output. Within a struct you can normally use the following function signature:
struct Player {
std::string firstName{};
std::string name {};
unsigned int age{};
double score{};
// Extraction (input)
friend std::istream& operator >> (std::istream& is, Player& p);
// Insertion (output)
friend std::ostream& operator << (std::ostream& os, const Player& p);
};
You need to program the details of these functions.
So, next, the Team. Here you need many objects of the same Type, so many "Players".
You could write std::vector<Player> team{}; to define a team having many players.
And a complete example for the stuff learned so far:
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
struct Player {
std::string firstName{};
std::string name{};
unsigned int age{};
double score{};
// Extraction (input)
friend std::istream& operator >> (std::istream& is, Player& p) {
std::getline(is >> std::ws, p.firstName);
std::getline(is, p.name);
return is >> p.age >> p.score;
}
// Insertion (output)
friend std::ostream& operator << (std::ostream& os, const Player& p) {
return os << p.firstName << ' ' << p.name << ' ' << p.age << ' ' << p.score;
}
};
int main() {
// Define the team
std::vector<Player> team{};
// Give user instructions and get number of players for input
std::cout << "How many players do you want to enter?\n";
int numberOfPlayers{}; std::cin >> numberOfPlayers;
// Get the player data and add to team
for (int i = 0; i < numberOfPlayers; ++i) {
std::cout << "\n\nEnter player data << " << i + 1 << " One at a line : First name, name, age, score:\n";
Player player;
std::cin >> player;
team.push_back(player);
}
// Show complete team:
std::cout << "\n\nTeam:\n";
for (const Player& p : team)
std::cout << p << '\n';
}
Since you did not state your requirements very clearly, I cannot answer further. But you should have understood the basics now and can add further structs and arrays as needed.
Happy coding
"Write a class named Person that represents the name and address of a person. Use a string to hold each of these elements. Add operations to read and print Person objects to the code you wrote."
Note I haven't reached the section on access control yet.
#include <iostream>
#include <string>
using std::string;
struct Person
{
string name_var, address_var;
};
std::ostream &print(std::ostream&, const Person &);
std::istream &read(std::istream&, Person &);
std::istream &read(std::istream &is, Person &item)
{
is >> item.name_var >> item.address_var;
return is;
}
std::ostream &print(std::ostream &os, Person &item)
{
os << item.name_var << " " << item.address_var;
return os;
}
With this I can only read single worded names and addresses if I use std::cin as the first argument to read, which isn't very useful. Can you somehow use getline?
You can use:
std::getline(is, item.name_var);
You can also specify a delimiter char as the third argument
Look at one thing: you have used a space to separate name and address. Now, in order to know when the name is over, you need to use some other delimiter for name. Another delimiter means you need to enclose the name in e.g. double quote (") and then, while reading the name, get input until you get the second delimiter.
I am trying to extract information into class objects from a colon delimited file. Each line of the file is set up in the same format. Here are the first few lines of the file:
s:Charles:Babbage:80:530213286:1133764834:mechanical engineering:3.8
e:Marissa:Meyer:37:549114177:53321:ceo:4456000
s:Alonzo:Church:92:586312110:1100539644:mathematics:4.0
e:Dana:Ulery:74:573811211:23451:engineer:124569
This is a school project and the purpose is to teach us about class inheritance. We have a base class Person and two child classes Student and Employee. We are supposed to import and store the information for students into Student objects and employee into Employee objects. I have an array of objects for each class; I'm sorting the students into the array of Student objects, same for employees, and in addition adding all people to the array of People objects.
I don't know what to do to get each piece of information with the delimiting commas. Right now I'm trying to use .getline but it doesn't seem to be working. How do I use this function (or another function) to extract information between delimiters into char arrays? Here's what I have so far for the case that the data is for an employee:
ifstream fin;
char* tempImport;
tempImport = new char[50];
int* tempIntArray;
tempIntArray = new int[10];
double tempDouble;
int tempInt;
// get the specifier of student or employee
fin.getline(tempImport, ':');
if(tempImport[0]=='e'){
// get first name
fin.getline(tempImport, ':');
employees[employeeIndex].setFirstName(tempImport);
allPeople[personIndex].setFirstName(tempImport);
// get last name
fin.getline(tempImport, ':');
employees[employeeIndex].setFirstName(tempImport);
allPeople[personIndex].setFirstName(tempImport);
// get age
fin.getline(tempImport, ':');
employees[employeeIndex].setAge(tempImport[0] - 0);
allPeople[personIndex].setAge(tempImport[0] - 0);
// get SSN
fin.getline(tempImport, ':');
for(int i=0;i<9;i++){
tempIntArray[i] = tempImport[i] - 0;
}
employees[employeeIndex].setSsn(tempIntArray);
allPeople[personIndex].setSsn(tempIntArray);
// get Employee ID
fin.getline(tempImport, ':');
for(int i=0;i<5;i++){
tempIntArray[i] = tempImport[i] - 0;
}
employees[employeeIndex].setEmpID(tempIntArray);
// get title
fin.getline(tempImport, ':');
employees[employeeIndex].setTitle(tempImport);
// get salary
fin >> tempDouble;
employees[employeeIndex].setSalary(tempInt);
employeeIndex++;
personIndex++;
}
It looks like you're missing a parameter when you call ifstream::getline(). See:
http://www.cplusplus.com/reference/istream/istream/getline/
You need the 3-parameter version of the method in order to specify a delimeter. When you call the 2-parameter version it interprets ':' as the streamsize. (Basically, ':' just resolves to the ASCII code for a colon, so that number gets passed in. What you really want for streamsize is the length of your tempImport buffer.)
However, if I may suggest (and your assignment allows it), the std::getline() version of the function may be better. (It allows you to use std::string instead of char*, which is a more C++ish way of doing things. Also you don't have to worry about if your input is bigger than your buffer.) Here's the documentation on that:
http://www.cplusplus.com/reference/string/string/getline/
So basically you could do something like this:
std::string tempImport;
std::getline(fin, tempImport, ':');
As a debugging suggestion, you could print tempImport after each time you call getline() on it (regardless of which kind you use). Take those out before you submit, but those print statements could help you debug your parsing in the meantime.
std::stderr << "getline(): " << tempImport << std::endl;
Edit:
Regarding the comment below, I was able to get this to compile. (It doesn't do anything useful, but shows that std::getline() is indeed present and compiles.) Does it compile for you?
#include <fstream>
int main (int argc, char** argv)
{
std::ifstream ifs;
std::string str;
std::getline(ifs, str, ':');
return 0;
}
If you'll pardon my saying so, you seem to have been taught one of the worst possible imitations of 'object oriented programming' (though if it's any comfort, it's also a fairly common one).
Personally, I think I'd write things quite a bit differently.
I'd probably start by eliminating all the setSalary, setTitle, etc. They're a horrible perversion of what OOP was supposed to do, losing a great deal in readability while gaining nothing in encapsulation.
Rather than providing member functions to manipulate all the members of the class, the class should provide a higher-level member to reconstitute an instance of itself from a stream.
When you do get the data, you probably do not want to create separate objects for your arrays of People/Employees/Students. Rather, each item will go into the array of either Employees or Students. Then the People will be just an array of pointers to the items in the other two arrays.
As to the details of reading the data: personally I'd probably write a ctype class that classified : as whitespace, and then just read data. For your class, you probably want to stick to using getline though.
class Person {
virtual std::istream &read(std::istream &is);
friend std::istream &operator>>(std::istream &is, Person &p) {
return p.read(is);
}
};
class Student : public Person {
std::string first_name;
std::string last_name;
std::string age;
std::string ssn;
std::string ID;
std::string title;
std::string salary;
virtual std::istream &read(std::istream &is) {
std::getline(is, first_name, ':');
std::getline(is, last_name, ':');
std::getline(is, age, ':');
// ...
return is;
}
};
With those in place, reading data from the file will normally be pretty simple:
std::string t;
Employee e;
Student s;
while (std::getline(infile, t, ':'))
if (t == "e") {
infile >> e;
Employees.push_back(e);
}
else if (t =="s") {
infile >> s;
Students.push_back(s);
}
I want to make a class of a student and take 3 inputs information and make an output of this file. How to this? This is my try:
#include <iostream>
using namespace std;
class Student{
private:
char name[50];
char id[50];
int age;
public:
void getdata()
{
//take name as input
//take id as input
//take age as input
}
void showdata()
{
//display stored file
}
}
int main()
{
Student s1;
ofstream s1("student.txt"); //i want to store that 's1' object
//anything else
return 0;
}
C++ uses the stream paradigm to implement standard input/output.
Stream paradigm means that if you application wants to access/use a resource (A file, the console, etc) a stream acts as a mediator between your application and the resource:
ofstream +----+
+-------------------->|File|
| +----+
|
+------+------+
| Application |
+------+------+
|
| +-------+
+-------------------->|Console|
cout +-------+
It means every write/read operations you perform are really stream operations. The point of this is that stream operations are basically the same, independently of what type of resource (And what type of stream) are you using.
This allows us to implement a "generic" (Generic meaning valid for any type of stream/resource). How? Overloading C++ operators >> and <<.
For input operations (Input means receiving data from the stream and put it in our variable/object), we need to overload the >> operator as follows:
istream& operator>>(istream& is , MyClass& object)
{
is >> object.myClassAtributte; (1)
... //Same for every attribute of your class.
return is;
}
First, note that the input stream is passed by reference. By reference because streams are non-copyable (What exactly means to copy a stream? Copy the link between your app and the resource? That sounds ridiculous), and non-const beacuse you are going to modify the stream (You are going to write through it).
Finally, note that the function not returns void, returns a reference to the same stream that was passed to the function. That allows you to write concatenated-write/read sentences like cout << "Hello. " << "Im" << " Manu343726" << endl;
For output operations (Output means sending data to the stream), we need to overload the << operator, wich implementation is exactly the same:
ostream& operator<<(ostream& os , const MyClass& object)
{
os << object.myClassAtributte; (1)
... //Same for every attribute of your class.
return os;
}
Note that in this case, your object is passed const, beacuse we won't modify it (We will only read its attributes).
(1) Is preferable to implement this functions making them friend of your class, to allow us access to private/protected members.
You have to overload << and >> operators for ostream and istream
std::ostream& operator<< (std::ostream& stream, const Student& student)
std::istream& operator<< (std::istream& stream, Student& student)
make them friend and implement
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to serialize in c++ ?
I have a class
Class Person
{
int age;
char *name;
char* Serialize()
{
//Need to convert age and name to char* eg:21Jeeva
}
void DeSerialize(char *data)
{
//Need to populate age and name from the data
}
};
In C# we can use MemoryStream,BinrayWriter/BinaryReader to achieve this. In c++ somewhere i found we can use iostream to achieve it. But couldnt able to get a proper example of it.
The purpose of the code is after calling serialize i am going to send the data over socket and in receiving end ill call DeSerialize to populate the members back.
You could take a look at Boost.Serialization. If you only need a simple text-serialization based on iostreams, you probably want to overload the stream extraction operators. For serialization this could look like this:
std::ostream & operator<<(std::ostream & stream, const Person & person) {
stream << person.age << " " << person.name;
return stream;
}
You have to make this function a friend of Person for this to work.
For deserialization, you would use this:
std::istream & operator>>(std::istream & stream, Person & person) {
stream >> person.age >> person.name;
return stream;
}
Using these, you can do the following:
Person fred;
fred.name = "Fred";
fred.age = 24;
std::stringstream buffer;
buffer << fred << std::endl;
Person fred_copy;
buffer >> fred;
You can find a small working example here.
Overloading these operators has the advantage that you can for instance use std::copy with an std::ostream_iterator to serialize an entire collection of Persons in one statement.
You can use following wrapper to convert any datatype to character stream:
template<class TYPE>
string ToChar (const TYPE& value)
{
stringstream ss;
ss << value;
return ss.str();
}
You can use the string object the way you want; like convert into char stream using c_str() or copy into array and so on.