I stored some strings in a text file using overloaded insertion operator.
ostream & operator << (ostream & obj,Person & p)
{
stringstream ss;
ss << strlen(p.last) << p.last << strlen(p.first) << p.first
<< strlen(p.city) << p.city << strlen(p.state) << p.state;
obj << ss.str();return obj;
}
The contents of file look like this
4bill5gates7seattle10washington
I now need to read the length first and display the string.And continue to display all the strings.How to do this with an overloaded extraction operator?
Read it one character at a time and use std::string::push_back to append to a string variable. There's a std::stoi which will convert your string lengths to an integer. Might I suggest that when you create your text file that you put a whitespace after your integer length, then you can just cin >> string_length and avoid using if statements to control when you've found the end of a number, or the beginning of a new string.
Also, it would be more beneficial, if you showed us what you've attempted, so that we could help you more specifically.
You may do:
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>
int main() {
std::istringstream in("4bill5gates7seattle10washington");
std::vector<std::string> strings;
unsigned length;
while(in >> length) {
std::string s;
if(in >> std::setw(length) >> s)
strings.push_back(s);
}
for(const auto& s : strings)
std::cout << s << '\n';
}
Disclaimer: The file format is evil.
Note: This does not extract a 'Person', but fields. I leave that to you.
Make operator << like this
ostream & operator >> ( ostream & obj, Person & p )
{
obj << strlen( p.last ) << " " << p.last << " " << strlen( p.first ) << " " << p.first << " "
<< strlen( p.city ) << " " << p.city << " " << strlen( p.state ) << " " << p.state;
return obj;
}
and operator >> like this
istream & operator >> ( istream & obj, Person & p )
{
obj >> p.last >> p.first >> p.city >> p.state;
return obj;
}
Related
I would like to emphasize on a fundamental question below:
Assume you have a CSV file and fill cells with Input Headers
The code should read this from .csv file and write the results into .csv file. It is nice to also code output total number of cases, plus average of cases. Here is a taken sample form SO and I would like to see how this can be efficiently adopted to complete this basic example.
void create()
{
// file pointer
fstream fout;
// opens an existing csv file or creates a new file.
fout.open("reportcard.csv", ios::out | ios::app);
cout << "Enter the details of 5 students:"
<< " roll name maths phy chem bio";
<< endl;
int i, roll, phy, chem, math, bio;
string name;
// Read the input
for (i = 0; i < 5; i++) {
cin >> roll
>> name
>> math
>> phy
>> chem
>> bio;
// Insert the data to file
fout << roll << ", "
<< name << ", "
<< math << ", "
<< phy << ", "
<< chem << ", "
<< bio
<< "\n";
}
}
Also, Read a particular record
void read_record()
{
// File pointer
fstream fin;
// Open an existing file
fin.open("reportcard.csv", ios::in);
// Get the roll number
// of which the data is required
int rollnum, roll2, count = 0;
cout << "Enter the roll number "
<< "of the student to display details: ";
cin >> rollnum;
// Read the Data from the file
// as String Vector
vector<string> row;
string line, word, temp;
while (fin >> temp) {
row.clear();
// read an entire row and
// store it in a string variable 'line'
getline(fin, line);
// used for breaking words
stringstream s(line);
// read every column data of a row and
// store it in a string variable, 'word'
while (getline(s, word, ', ')) {
// add all the column data
// of a row to a vector
row.push_back(word);
}
// convert string to integer for comparision
roll2 = stoi(row[0]);
// Compare the roll number
if (roll2 == rollnum) {
// Print the found data
count = 1;
cout << "Details of Roll " << row[0] << " : \n";
cout << "Name: " << row[1] << "\n";
cout << "Maths: " << row[2] << "\n";
cout << "Physics: " << row[3] << "\n";
cout << "Chemistry: " << row[4] << "\n";
cout << "Biology: " << row[5] << "\n";
break;
}
}
if (count == 0)
cout << "Record not found\n";
}
[1]: https://i.stack.imgur.com/q6VfZ.png
I've mainly concentrated on adding overloads for operator<< and operator>> since you showed some interest in those earlier and describe what they are doing in comments in the code.
Since you are mixing input and output from streams that are comma separated and other streams I've added an adapter for CSV streaming as well as overloads for streaming to/from a user.
First, create a class to keep all data that belongs together in one data record. I've made it a simple struct here, which is a class with public access to its members per default.
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
// your student record
struct student {
std::string name; // It's usually good to have larger types first so name goes first
int roll;
int math;
int phy;
int chem;
int bio;
};
// read a student from an istream (like std::cin) - whitespace separated
std::istream& operator>>(std::istream& is, student& s) {
return is >> s.roll >> s.name >> s.math >> s.phy >> s.chem >> s.bio;
}
// write a student to an ostream (like std::cout)
std::ostream& operator<<(std::ostream& os, const student& s) {
return os << "Details of Roll " << s.roll << ":\n"
<< "Name: " << s.name << '\n'
<< "Maths: " << s.math << '\n'
<< "Physics: " << s.phy << '\n'
<< "Chemistry: " << s.chem << '\n'
<< "Biology: " << s.bio << '\n';
}
//--------------------------------------------------------------------------------------
// An adapter for comma separated streaming
struct CSVStudent {
CSVStudent(student& s) : stud(s) {}
CSVStudent(const CSVStudent&) = delete;
// The CSVStudent holds a reference to a "student"
student& stud;
};
// read a record from an istream - comma separated
std::istream& operator>>(std::istream& is, CSVStudent& csvstud) {
std::string line;
student& s = csvstud.stud; // an alias to the student to have to type less
if(std::getline(is, line)) { // read a complete line
// put the line in an istringstream for extraction:
std::istringstream ss(line);
char delim; // a dummy for reading commas
// Extract the comma separated values. "delim" is not checked so it could be
// any char breaking up the int:s.
//
// The below does things in the following order:
// 1. "ss >> s.roll >> delim"
// This extracts roll and a comma and returns
// a reference to ss, which is used in 2.
// 2. std::getline(ss, s.name, ',')
// Extracts a string until a comma is encountered.
// 3. Normal extraction for the rest of the int:s with the
// dummy variable "delim" where the commas are supposed to be.
if(not(std::getline(ss >> s.roll >> delim, s.name, ',') >> s.math >> delim >>
s.phy >> delim >> s.chem >> delim >> s.bio)) {
// If we get here, the extraction from the istringstream failed, so set
// the failstate on the istream too. Note the "not" on the line above.
is.setstate(std::ios::failbit);
}
}
return is;
}
// write a record to an ostream - comma separated
std::ostream& operator<<(std::ostream& os, const CSVStudent& csvstud) {
const student& s = csvstud.stud;
os << s.roll << ',' << s.name << ',' << s.math << ',' << s.phy << ',' << s.chem
<< ',' << s.bio << '\n';
return os;
}
//--------------------------------------------------------------------------------------
// get all students in the file as a std::vector<student>
std::vector<student> read_student_file(const std::string& filename) {
std::vector<student> retval;
std::ifstream fin(filename);
if(fin) { // file opened successfully
student stud;
CSVStudent csvstud{stud}; // holds a reference to stud
// loop for as long as student records can be read successfully
while(fin >> csvstud) // use the csv sdapter
retval.push_back(stud); // and put the stud in the vector
}
return retval;
}
//--------------------------------------------------------------------------------------
void create(const std::string& filename) {
// open an existing csv file or creates a new file.
std::ofstream fout(filename, std::ios::out | std::ios::app);
if(fout) {
std::cout << "Enter the details of 5 students:"
" roll name maths phy chem bio\n";
// Read the input
for(int i = 0; i < 5; i++) {
student stud;
std::cout << (i + 1) << ": ";
if(std::cin >> stud) {
// Insert the data to file if one was entered successfully
fout << CSVStudent(stud); // uses the adapters operator<<
} else {
std::cerr << "You failed to enter data for student " << (i + 1) << '\n';
break;
}
}
}
}
//--------------------------------------------------------------------------------------
int main() {
std::string filename = "reportcard.csv";
std::vector<student> students = read_student_file(filename);
std::cout << "There are " << students.size() << " students in the file.\n";
if(not students.empty()) {
// show the last record if there are any records in the file
std::cout << "Record " << students.size() << " is:\n\n";
std::cout << students.back() << '\n';
}
// create 5 new records
create(filename);
}
If reportcard.csv contains this:
1,Ted,1,2,3,4
2,Foo,2,3,4,5
3,Bar,3,4,5,6
4,Baz,4,5,6,7
5,Bork,5,6,7,8
The program should start up like this:
There are 5 students in the file.
Record 5 is:
Details of Roll 5:
Name: Bork
Maths: 5
Physics: 6
Chemistry: 7
Biology: 8
Enter the details of 5 students: roll name maths phy chem bio
1: <and here is where you're supposed to enter the first of 5 new students>
I have a problem with fill container map with information . I read the information with operator>> but it gives me these errors in my second class CDealer. no operator ">>" matches these operands and binary">>": no operator found which takes a right-hand operand of type 'const CStock'(or these is no acceptable conversion)
I have three classes: CStock, CDealer who have map<CStock, pair<unsigned, double>> Stock; and CShop who have vector<CDealer*> Dealers;
I'll be very thankful if someone can help me.
This is my operators << and >> in CStock
ostream &operator << (ostream &toStream, const CStock &S){
toStream << "Name Stock: " << S.m_strName_stock << " Producer: " << S.m_strProducer <<
"Date: " << S.Date;
return toStream;
}
istream &operator >> (istream &fromStream, CStock &S)
{return fromStream >> S.m_strName_stock >> S.m_strProducer >> S.Date;}
This is my operators << and >> in CDealer
`
ostream &operator << (ostream &toStream, CDealer &D)
{
toStream << "Name Dealer: " << D.m_strName_Dealer << " Agent: " << D.m_strAgent <<
"Address: " << D.m_strAddress;
map<CStock, pair<unsigned, double>>::const_iterator it = D.Stock.begin();
while (it != D.Stock.end())
{
toStream << it->first <<it->second.first << it->second.second;
}
return toStream;
}
istream &operator >> (istream &fromStream, CDealer &D)
{
map<CStock, pair<unsigned, double>>::iterator it = D.Stock.begin();
fromStream >> D.m_strName_Dealer >> D.m_strAgent >> D.m_strAddress;
while (it!= D.Stock.end())
{fromStream >> it->first >> it->second.first >> it->second.second;}
return fromStream
}
And this is the constructor with parameter: file name and these tho operators <<, >>
CShop::CShop(const string &fname)
{
CDealer c;
fstream File(fname, ios::in);
if (File.is_open())
{
File >> m_strNameShop;
File >> m_strCity;
while (File>>c)
{
Dealers.push_back(new CDealer(c));
}
File.close();
}
else
throw "ERROR! ";
}
ostream &operator << (ostream &toStream, const CShop &S)
{
toStream << "Name Shop: " << S.m_strNameShop << " City: " << S.m_strCity;
vector<CDealer* >::const_iterator it = S.Dealers.begin();
while (it != S.Dealers.end())
{
CDealer* dealerPtr = *it++;
toStream << *dealerPtr<< endl;
}
return toStream;
}
istream &operator >> (istream &fromStream, CShop &D)
{
return fromStream >> D.m_strNameShop >> D.m_strCity;
}
And in the end main()
#include"CDealer.h"
#include"CShop.h"
#include<iostream>
#include<string>
#include <stdlib.h>
#include<vector>
using namespace std;
int main()
{
CShop SS1("data.txt");
cout << SS1;
system("pause");
return 0;
}
The implmentation of istream &operator >> (istream &fromStream, CDealer &D)
is poorly thought through.
The objective of the function is to read data from an input stream and flesh out the contents of the CDealer object. Normally the operator<< and operator>> functions work in tandem so that what you write using operator<< can be read back using operator>>.
From that point of view, even the operator<< function needs to revamped.
Here's one implementation that should work as long as there are no spaces in the string objects. If you have space in your string objects, the code needs be changed.
std::ostream& operator<<(std::ostream &toStream, CDealer cosnt& D)
{
// Write the name of the dealer, without any decorative text.
// Add a space character to the output so you can read the name back.
toStream << D.m_strName_Dealer << " ";
// Do the same for name of the agent and address.
toStream << D.m_strAgent << " ";
toStream << D.m_strAddress << " ";
// Write the number of items in Stock first.
// This will be necessary when reading the data.
toStream << D.Stock.size() << " ";
// Now write the Stock items, which spaces between each field you write.
map<CStock, pair<unsigned, double>>::const_iterator it = D.Stock.begin();
while (it != D.Stock.end())
{
toStream << it->first << " " << it->second.first << " " << it->second.second << " ";
}
return toStream;
}
std::istream &operator>>(std::istream &fromStream, CDealer &D)
{
// Read the name of the dealer.
fromStream >> D.m_strName_Dealer;
// Read the name of the agent.
fromStream >> D.m_strAgent;
// Read the address.
fromStream >> D.m_strAddress;
// Read the number of items.
size_t num;
fromStream >> num;
// Now read the Stock items.
for ( size_t i = 0; i < num; ++i )
{
// Obtained the types for key, val1, and val2 from
// the error message posted in a comment by OP.
CStock key;
int val1;
double val2;
fromStream >> key >> val1 >> valu2;
// Add the item to Stock.
D.Stock.insert(std::make_pair(key, std::make_pair(val1, val2)));
}
return fromStream
}
Problem
The problem can be reduced to the following case:
#include <map>
int main(void) {
std::map<int, int> val;
auto it = val.begin();
it->first = 10;
}
The key values in a std::map are const (the pair of key and value is defined as std::pair<const Key, T>), so for
map<CStock, pair<unsigned, double>>
we have a map containing pairs of
std::pair<const CStock, pair<unsigned, double>>
which makes it->first in
fromStream >> it->first >> it->second.first >> it->second.second;
a const CStock and results in the reported error.
This all makes sense because the map is ordered by the key, so if you could change the key whenever you wanted to whatever you wanted the map would soon be incoherent.
Documentation on std::map
Solution
There is no simple solution. You cannot do this. You must either make the items and then place them in the map or use one of the emplace family of functions to construct them directly in the map. Once they are in the map you can change the value, but not the key. You could remove the item, and thus the key, and reinsert with a different key, but why are you doing this in the first place? I recommend a rethink of your logic.
I have an assignment that wants me to write a program that reads a text file, then outputs a modified version of that file using a version of Ceasar cipher that shifts the characters of the file that the user calls based on the shift amount that they input. For example if my file reads "hi" and they shift it by 1 it should read "ij". But when I run the program it doesnt recognize when I call in1 or out1 and I just get an error message. Can anyone help me figure out what I'm doing wrong or offer any advice on how to move forward? Thank you in advance!
#include <iostream>
#include <iomanip>
#include <fstream>
#include <cctype>
using namespace std;
int main()
{
//Declare user variables
int shift, file1chars = 0;
char filename[25];
ifstream in1;
ofstream out1;
do
{
in1.clear(); //clear status flags
//Prompt user to enter name of input file and amount of shift
cout << "Please enter the name of the input file." << endl;
cout << "Filename: ";
cin >> filename;
//Open file name
in1.open(filename);
//Error message if no file
if (!in1)
cout << "That is not a valid file. Try again\n";
} while (!in1);
do
{
out1.clear(); //clear status flags
//prompt user to input out file
cout << "Please enter the name of the output file." << endl;
cout << "Filename: ";
cin >> filename;
out1.open(filename);
//Error message if no file
if (!out1)
cout << "That is not a valid file. Try again\n";
} while (!out1);
//write some code to the input file
in1 >> "hi"
//write the integers in a different order to the output file
out1 << //idk where to go from here to initiate shift
//prompt user to enter shift
cout << "Please intput the shift amount: ";
cin >> shift;
cout << "Processing complete" << endl;
//Call file (?)
//Tell user file input is complete and is now printing statistics
cout << "\nShifted input file Complete. Now printing statistics " << endl;
//Show statistics for file
cout << "\nStatistics for file: " << filename << endl;
cout << "------------------------------------------------";
//Show characters in file and stats before shift
cout << "\n\nTotal # of characters in file: " << file1chars << endl;
cout << "Statistics before shift: " << endl;
//Show file before shift
//Show user stats after shift
cout << "\nStatistics after shift: " << endl;
//File after shift
//Close files
out1.close();
in1.close();
return 0;
}
Instead of looking at your code for line by line to see where the problem(s) could be, I ask you to think about your requirements and write code that expresses the intent as closely as possible. Create functions that follow the intended functionality.
Get the input file name.
Get the output file name.
Read the contents of the input file.
Transform the contents of the input file to create the output string.
Write the output string to the output file.
In pseudo code,
int main()
{
infile = get_input_filename()
outfile = get_output_filename()
contents = get_file_contents(infile)
output = transform_input(contents)
write_output(outfile, output)
}
Convert that to C++ code:
// Declare the functions.
std::string get_input_filename();
std::string get_output_filename();
std::string get_file_contents(std::string const& infile)
std::string transform_input(std::string const& contents)j
void write_output(std::string const& outfile, std::string const& output);
// Use them in main.
int main()
{
std::string infile = get_input_filename();
std::string outfile = get_output_filename();
std::string contents = get_file_contents(infile);
std::string output = transform_input(contents);
write_output(outfile, output);
}
// Implement the functions.
std::string get_input_filename()
{
std::string filename;
cout << "Please enter the name of the input file." << endl;
cout << "Filename: ";
cin >> filename;
return filename;
}
std::string get_output_filename()
{
std::string filename;
cout << "Please enter the name of the output file." << endl;
cout << "Filename: ";
cin >> filename;
return filename;
}
std::string get_file_contents(std::string const& infile)
{
std::ifstream inf(infile);
std::string contents;
int c;
while ( (c = inf.get()) != EOF )
{
contents += c;
}
return contents;
}
std::string transform_input(std::string const& contents)
{
std::string res;
// Do the needful to transform contents to res.
return res;
}
void write_output(std::string const& outfile, std::string const& output)
{
std::ofstream outf(outfile);
outf.write(output.c_str(), output.size();
}
If you are able to use a class or struct and functions I would propose something like this:
main.cpp
#include <string>
#include <iostream>
#include <fstream>
#include "CaesarShift.h"
int main() {
std::string filename;
std::cout << "Please enter the name of the input file. ";
std::cin >> filename;
std::ifstream fileIn;
std::string text;
fileIn.open( filename );
if ( !fileIn.is_open() ) {
std::cout << "Failed to open file: " << filename << "." << std::endl;
}
fileIn >> text;
fileIn.close();
CaesarText caesarText;
caesarText.addText( text );
std::cout << "Contents of the Caesar Text before peforming a Caesar Shift:\n"
<< caesarText << std::endl;
int amount = 0;
std::cout << "Please enter the amount to shift the text ";
std::cin >> amount;
std::cout << "Now performing the Caesar Shift: " << std::endl;
caesarShift( caesarText, amount );
std::cout << "Caesar Text after performing a Caesar Shift:\n"
<< ceasarText << std::endl;
std::ofstream fileOut;
fileOut.open( std::string( "shifted_" + filename ) );
if ( !fileOut.is_open() ) {
std::cout << "Failed to open shifted_" << filename << std::endl;
}
// Uncomment to print original text to file otherwise only modified text will be printed.
// fileOut << caesarText.originalText() << std::endl;
fileOut << caesarText.shiftedText() << std::endl;
fileOut.close();
system( "PAUSE" );
return 0;
}
CaesarShift.h
#ifndef CAESAR_SHIFT_H
#define CAESAR_SHIFT_H
class CaesarText {
std::string _originalText;
std::string _shiftedText;
public:
caesarText() = default;
explicit CeasarText( const std::string& text ) :
_originalText( text ) {}
void addText( const std::string& text ) {
_originalText = text;
}
std::string originalText() const {
return _originalText;
}
std::string shiftedText() const {
return _shiftedText;
}
friend void caesarShift( caesarText& c, int amount );
friend std::ostream& operator<<( std::ostream& out, const caesarText& ceasarText );
};
#endif // !CAESAR_SHIFT_H
CaesarShift.cpp
#include "CaesarShift.h"
#include <string>
#include <iostream>
#include <algorithm>
// Overloaded ostream operator
std::ostream& operator<<( std::ostream& o, const CaesarText& c ) {
o << "Original Text: " << c._originalText << "\n";
o << "Shifted Text: " << c._shiftedText << "\n";
return o;
}
// public friend function (visible in main) not actually a part of the class
// but performs operations on it.
// To perform the Caesar Shift here are 3 possible variations of implementing the same task.
void caesarShift( Caesar Text& text, int amount ) {
// Bound amount to the number of characters in the alphabet
// the same value used in any of the three variations below.
amount %= 26;
// Older c++ style loop.
/*for ( std::size_t i = 0; i < text._originalText.length(); i++ ) {
char c = text._originalText[i] + amount;
text._shiftedText += c;
}*/
// Modern C++ style loop
/*for ( auto& c : text._originalText ) {
text._shiftedText += c + amount;
}*/
// std::transform( s1.begin, s1.end, back_inserter( s2 ), lamda as predicate );
/*std::transform( text._originalText.begin(), text._originalText.end(),
std::back_inserter( text._shiftedText ),
[amount]( unsigned char c ) -> unsigned char { return c + amount; }
);*/
}
As for the 3 different variations of performing the Caesar Shift you can just uncomment the appropriate block section.
In my lab I'm trying to process a line that represents a destination on Earth. An example of this is ....
"152/N 200/E California Ave"
In my professor's notes, he said that if you did this ...
std::cin >> latitude >> latitudeDirection >> longitude >> longitudeDirection >> address
Which consumes all the way up until California, where from there on out the string is consumed one at a time at each white space. How do I make it so I consume the rest of the input? Here's how my variables look when assigned ...
latitude = 152
latitudeDirection = "/N"
longitude = 200
longitudeDirection = "/E"
address = "California"
address only holds "California" when I want it to hold "California Ave". Here's the code I have so far.
int numberOfLocations;
std::cin >> numberOfLocations;
std::cin.ignore();
for (int x = 0; x < numberOfLocations; x++) {
double longitude, latitude;
std::string longitudeDirection, latitudeDirection, address;
/*
std::cin >> latitude >> latitudeDirection >> longitude >> longitudeDirection >> address;
std::cout << latitude << latitudeDirection << latitude << latitudeDirection << address << std::endl;
*/
}
The ordinary >> as input operator splits the input into "words" separated by whitespace.
To avoid that, consider reading one item per line of input. You can use std::getline from <string> to read a line. Where you need a number you can parse the line with e.g. std::stoi.
If you have no choice but to deal with a string that contains multiple items of input, where at the end there's textual data that can contain spaces, then you can use a std::istringstream to read the items before the text.
Example 1 – reading one item per line:
#include <iostream>
#include <stdlib.h> // exit, EXIT_FAILURE
#include <string> // std::string
using namespace std;
void error( string const& s )
{
cerr << "!" << s << endl;
exit( EXIT_FAILURE );
}
auto get_input_line()
-> string
{
string line;
getline( cin, line );
if( cin.fail() ) { error( "Uh oh, ..." ); }
return line;
}
auto main() -> int
{
cout << "This program adds two numbers A and B." << endl;
cout << "Number A, please? ";
double const a = stod( get_input_line() );
cout << "Number B, please? ";
double const b = stod( get_input_line() );
cout << a << " + " << b << " = " << a + b << "." << endl;
}
Example 2 – parsing a line of text
(IMHO not smart, but perhaps a requirement)
#include <iostream>
#include <stdlib.h> // exit, EXIT_FAILURE
#include <string> // std::string
#include <sstream> // std::istrstream
using namespace std;
void error( string const& s )
{
cerr << "!" << s << endl;
exit( EXIT_FAILURE );
}
auto main() -> int
{
string const data = "152/N 200/E California Ave";
cout << "This program parses the line '" << data << "'." << endl;
cout << endl;
cout << "Result:" << endl;
istringstream stream( data );
char dummy_char;
int latitude;
char latitudeDirection;
int longitude;
char longitudeDirection;
string address;
stream
>> latitude >> dummy_char >> latitudeDirection
>> longitude >> dummy_char >> longitudeDirection;
if( stream.fail() ) { error( "Initial items extraction failed." ); }
while( stream.peek() == ' ' ) { stream.get(); }
getline( stream, address );
cout << "> "
<< latitude << "/" << latitudeDirection << " "
<< longitude << "/" << longitudeDirection << " "
<< "'" << address << "'"
<< "<" << endl;
}
The problem here is that, you are trying to enter strings with space, therefore, as inputs via cin are delimited by a space ' ', you will have to use std::getline which will make the end of input delimeter as newline.
Example Implementation:
#include <iostream>
using namespace std;
int main()
{
string s;
getline(cin,s);
cout<<s;
return 0;
}
with this you can enter string with spaces. (end of string will be after a n enter pressed by user, there for end of string delimiter is newline)
Say I have a structure as follows
struct stock{
string ticker;
double price;
double volume;
double eps;
};
If I want to output one of the variables such as price when asked for it would I have to do a large if/else or switch statement to match up the user input with the member or is there a more elegant way to do it because I know stock.userInput does not work.
there's no special keyword to find your variable(sorry to burst your bubble), You would have to use a logical statement. It would go along:
cout << "What would you like to see? (1)Price (2)etc...etc...";
cin >> input;
switch(input)
{
case 1:
cout << Obj.Price;
break;
case 2:
cout << //....
break;
}
I personally like using keys and a switch statement, it tends to be a lot cleaner and easier to go back and modify later in the program.
struct stock s1;
cout<<" price is:"<< s1.price;
If you want to get rid of the large switch/if statement, you can use map with string and a pointer-to-member. Assuming your stock struct, you can use:
Define the map (for doubles here) and initialize it:
std::map<std::string,double stock::*> double_members;
double_members["price"]=&stock::price;
double_members["volume"]=&stock::volume;
double_members["eps"]=&stock::eps;
And use it to look up some values:
stock stock1;
std::string input;
std::cin >> input;
if (double_members.find(input)!=double_members.end())
std::cerr << "Value for " << input << " is: " << stock1.*double_members[input] << std::endl;
else
std::cerr << "There's no field called " << input << std::endl;
It's limited to a single type, but you can't have a statement like std::cerr << A; and have A's type resolved during runtime. If you care only about string (or any other, but always the same) representation of the values, then you can wrap maps for different types in a class that searches all of them and outputs the value converted to a string (or something).
But it's probably easier to have the if statement, unless the struct is really big.
If it's okay with you that it doesn't work with g++ 4.7.1 and earlier (but does work with Visual C++ 11.0 and later), then like …
#include <sstream> // std::ostringstream
#include <string> // std::string
using namespace std;
struct Stock
{
string ticker;
double price;
double volume;
double eps;
string toString() const
{
ostringstream stream;
stream
<< "Stock("
<< "ticker='" << ticker << "', "
<< "price=" << price << ", "
<< "volume=" << volume << ", "
<< "eps=" << eps
<< ")";
return stream.str();
}
Stock(): ticker(), price(), volume(), eps() {}
};
#include <iostream>
#include <regex>
#include <stdexcept>
#include <stdlib.h>
bool err( string const& s )
{
cerr << "!" << s << endl;
exit( EXIT_FAILURE );
}
string lineFromUser( string const& prompt )
{
string line;
cout << prompt;
getline( cin, line )
|| err( "oh my, failed to read line of input" );
return line;
}
void cppMain()
{
Stock stock;
stock.price = 1.23;
string const s = stock.toString();
cout << s << endl;
string const fieldname = lineFromUser( "Which field u want? " );
regex const valuespec( fieldname + "\\s*\\=\\s*([^,\\)]*)" ); //
smatch what;
if( regex_search( s, what, valuespec ) )
{
cout << "As we all know already, " << what.str() << "." << endl;
}
else
{
cout
<< "!Sorry, there's no field named '"
<< fieldname << "'"
<< endl;
}
}
int main()
{
try
{
cppMain();
return EXIT_SUCCESS;
}
catch( exception const& x )
{
cerr << "!" << x.what() << endl;
}
return EXIT_FAILURE;
}
Example usage:
[d:\dev\test]
> foo
Stock(ticker='', price=1.23, volume=0, eps=0)
Which field u want? eps
As we all know already, eps=0.
[d:\dev\test]
> _