Making a read function that allows spaces in the input (C++) - c++

"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.

Related

C++ Vectors with Array Elements?

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

Reading word by word from a file with operator >>

A few hours ago I asked how I could read from a file with a specific format so I could program operator >>
The format of the file was:
Salad;Tomatoe 50;Fresh lettuce 100;Potatoe 60;Onion 10
Macaroni;Macaroni 250;Tomatoe 60;Oil 10
Fish and chips;fish 30;potatoe 30;Oil 40
And I have the following class:
...
#include <list> //I'm using list of the STL
....
class recipe{
private:
list<pair<string,unsigned int>> ing; //A list of the ingredients of one recipe. String for the name of the ingredient and unsigned int for the quantity of each ingredient
public:
....
//My solution for operator >>
istream & operator >> (istream &i, recipe &other){
string line, data, name_ing;
string code, nombre;
unsigned int plate, quantity;
list<pair<string,unsigned int>> ings;
getline(i,line);
stringstream s (line);
getline(s,data,';');
code = data;
getline(s,data,';');
plate = atoi(data.c_str());
getline(s,data,';');
name = data;
while(getline(s,data,' ')){
name_ing = data;
getline(s,data,';');
quantity = atoi(data.c_str());
pair<string,unsigned int> ingredient;
ingredient.first = name_ing;
ingredient.second = quantity;
ings.push_back(ingredient);
}
recipe a_recipe(code,plate,name,0,0,0,0,0,ings);
oher = a_recipe;
return i;
}
So now I have another problem, I don't know how to read those ingredients that are composed by two words, for example: "fresh lettuce 50", because the output would be:
Salad;Tomatoe 50;Fresh 0;Potatoe 60;Onion 10
It doesn't read Lettuce and the quantity. Any help?
As already written:
To solve the problem at hand there is a more or less standard approach. You want to read csv data.
In your case, it is a little bit more difficult, because you do have nested csv data. So first a ";" separated list and then a space separated list. The 2nd one is a little bit unprecise, because our ingredients coud have 2 spaces before the quantity, like in "Red pepper 2"
Now, how could this to be done? C++ is an object oriented language. You can create objects, consisting of data and member functions that operate on this data. We will define a class "Recipe" and overwrite the inserter and extractor operator. Because the class and only the class should know how this works. Having done that, input and output becomes easy.
The extractor, and that is the core of the question is, as said, a little bit more tricky. How can this be done?
In the extractor we will first read a complete line from an std::istream using the function std::getline. After having the line, we see a std::string containing "data-fields", delimited by a semicolon. The std::string needs to be split up and the "data-fields"-contents shall be stored. Additionally you need to split the ingredients.
The process of splitting up strings is also called tokenizing. The "data-fields"-content is also called "token". C++ has a standard function for this purpose: std::sregex_token_iterator.
And because we have something that has been designed for such purpose, we should use it.
This thing is an iterator. For iterating over a string, hence sregex. The begin part defines, on what range of input we shall operate, then there is a std::regex for what should be matched / or what should not be matched in the input string. The type of matching strategy is given with last parameter.
1 --> give me the stuff that I defined in the regex and
-1 --> give me that what is NOT matched based on the regex.
We can use this iterator for storing the tokens in a std::vector. The std::vector has a range constructor, which takes 2 iterators a parameter, and copies the data between the first iterator and 2nd iterator to the std::vector.
The statement
std::vector token(std::sregex_token_iterator(line.begin(), line.end(), separator, -1), {});
defines a variable "token" of type std::vector<std::string>, splits up the std::string and puts the tokens into the std::vector. After having the data in the std::vector, we will copy it to the data members of our class.
For the 2nd split we create 2 simple lambdas and copy the data into the ingredients list.
Very simple.
Next step. We want to read from a file. The file conatins also some kind of same data. The same data are rows.
And as for above, we can iterate over similar data. If it is the file input or whatever. For this purpose C++ has the std::istream_iterator. This is a template and as a template parameter it gets the type of data that it should read and, as a constructor parameter, it gets a reference to an input stream. It doesnt't matter, if the input stream is a std::cin, or a std::ifstream or a std::istringstream. The behaviour is identical for all kinds of streams.
And since we do not have files an SO, I use (in the below example) a std::istringstream to store the input csv file. But of course you can open a file, by defining a std::ifstream csvFile(filename). No problem.
We can now read the complete csv-file and split it into tokens and get all data, by simply defining a new variable and use again the range constructor.
std::vector cookBook(std::istream_iterator<Recipe>(sourceFile), {});
This very simple one-liner will read the complete csv-file and do all the expected work.
Please note: I am using C++17 and can define the std::vector without template argument. The compiler can deduce the argument from the given function parameters. This feature is called CTAD ("class template argument deduction").
Additionally, you can see that I do not use the "end()"-iterator explicitely.
This iterator will be constructed from the empty brace-enclosed initializer list with the correct type, because it will be deduced to be the same as the type of the first argument due to the std::vector constructor requiring that.
Ì hope I could answer your basic question. Please see the full blown C++ example below:
#include <iostream>
#include <regex>
#include <string>
#include <list>
#include <vector>
#include <iterator>
#include <sstream>
// Data types for ingredients and quantity
using Ingredients = std::pair<std::string, int>;
// Some helper functions
auto trim = [](const std::string & s) { return std::regex_replace(s, std::regex("^ +| +$"), "$1"); };
auto split = [](const std::string & s) {size_t pos{ s.rfind(' ') }; return Ingredients(s.substr(0, pos), std::stoi(s.substr(pos))); };
std::regex separator{ ";" };
// Our recipe class
struct Recipe {
// data
std::string title{};
std::list<Ingredients> ingredients{};
// Overwrite extractor
friend std::istream& operator >> (std::istream& is, Recipe& r) {
// We will read one line into this temproary
std::string line{};
if (std::getline(is, line)) {
// Tokenize the base string
std::vector token(std::sregex_token_iterator(line.begin(), line.end(), separator, -1), {});
// get the recipe title
r.title = token[0];
// And, get the ingredients
r.ingredients.clear();
std::transform(std::next(token.begin()), token.end(), std::back_inserter(r.ingredients),
[](const std::string& s) { return split(trim(s)); });
}
return is;
}
// Overwrite inserter
friend std::ostream& operator << (std::ostream& os, const Recipe& r) {
// Print one recipe
os << "---- Recipe: " << r.title << "\n-- Ingredients:\n\n";
for (const auto& [ingredient, quantity] : r.ingredients)
os << ingredient << " --> " << quantity << "\n";
return os;
}
};
// Source file with CSV data. I added "Red Pepper 2" to Salad
std::istringstream sourceFile{ R"(Salad;Tomatoe 50;Lettuce 100;Potatoe 60;Red Pepper 2;Onion 10
Macaroni;Macaroni 250;Tomatoe 60;Oil 10
Fish and chips;fish 30;potatoe 30;Oil 40)" };
int main() {
// Read all data from the file with the following one-liner
std::vector cookBook(std::istream_iterator<Recipe>(sourceFile), {});
// Show some debug output
std::copy(cookBook.begin(), cookBook.end(), std::ostream_iterator<Recipe>(std::cout, "\n"));
return 0;
}
Again:
What a pity that nobody will read this . . .
I suggest that you make a type out of the ingredient and amount part instead of using std::pair<std::string, unsigned>. With that you can add stream operators for that type too (and not risk it being used by a different std::pair<std::string, unsigned> than the one you want to support). It breaks the problem down somewhat and makes it simpler to implement / understand.
That being that said, I suggest that you use something else than a space as a delimiter between the ingredient name and the amount since that complicates the parsing (as you can see in the code).
Here's an example with comments:
#include <cstdlib>
#include <iostream>
#include <list>
#include <sstream>
#include <string>
#include <tuple>
// a simple ingredient type
struct ingredient {
std::string name{};
unsigned amount{};
};
// read an ingredient "<name> <amount>"
std::istream& operator>>(std::istream& is, ingredient& i) {
std::string entry;
if(std::getline(is, entry, ';')) { // read until ; or EOL
// find the last space in "entry"
if(size_t pos = entry.rfind(' '); pos != std::string::npos) {
// extract the trailing amount
if(unsigned am = static_cast<unsigned>(
// Create a substring from the last space+1 and convert it to an
// unsigned (long). The static_cast<unsigned> silences a warning about
// the possibility to get the wrong value if it happens to be larger
// than an unsigned can hold.
std::strtoul(entry.substr(pos + 1).c_str(), nullptr, 10));
// and check that we extracted something else than zero
am != 0)
{ // extracted the amount successfully
i.name = entry.substr(0, pos); // put the name part in i.name
i.amount = am; // and the amount part in i.amount
} else { // extracting the amount resulted in 0
// set failbit state on is
is.setstate(std::ios::failbit);
}
} else { // no space found, set failbit
is.setstate(std::ios::failbit);
}
}
return is;
}
// output an ingredient
std::ostream& operator<<(std::ostream& os, const ingredient& i) {
return os << i.name << " " << i.amount;
}
class recipe {
public:
std::string const& name() const { return rname; }
// convenience iterators to iterate over ingreidiences, const
auto begin() const { return ing.cbegin(); }
auto end() const { return ing.cend(); }
// non-const if you'd like to be able to change an ingredient property while iterating
auto begin() { return ing.begin(); }
auto end() { return ing.end(); }
private:
std::list<ingredient> ing{}; // the new type in use
std::string rname{}; // recipe name
friend std::istream& operator>>(std::istream&, recipe&);
};
std::istream& operator>>(std::istream& i, recipe& other) {
std::string line;
if(std::getline(i, line)) {
std::istringstream ss(line);
if(std::getline(ss, other.rname, ';')) {
// only read the recipe's name here and delegate reading each ingredient
// to a temporary object of your new ingredient type
other.ing.clear(); // remove any prior ingrediences from other
ingredient tmp;
while(ss >> tmp) { // extract as normal
other.ing.push_back(tmp); // and put in ing if successful
}
}
}
return i;
}
// output one recipe in the same format as it can be read
std::ostream& operator<<(std::ostream& os, const recipe& other) {
os << other.name();
for(auto& i : other) {
os << ';' << i;
}
return os << '\n';
}
int main() {
std::istringstream is(
"Salad;Tomatoe 50;Fresh lettuce 100;Potatoe 60;Onion 10\n"
"Macaroni;Macaroni 250;Tomatoe 60;Oil 10\n"
"Fish and chips;fish 30;potatoe 30;Oil 40\n");
recipe r;
while(is >> r) {
std::cout << r;
}
}

Reading a word from a file overloading operator >>

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.

Writing a class object into a file using fstream and then read it

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

Serializing a c++ class [duplicate]

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.