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]
> _
Related
This question already has answers here:
Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?
(5 answers)
Closed 1 year ago.
I am trying to read data from a file to an array of struct, and then print it.
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct match {
string id;
string team1;
string team2;
string date;
string time;
string league;
};
void getmatches() {
int i = 0;
const int size = 50;
struct match {
string id;
string team1;
string team2;
string date;
string time;
string league;
};
match getmatches[100];
fstream show;
show.open("matches.txt");
while (! show.eof () && i < 10 )
{
getline(show, getmatches[i].id);
getline(show, getmatches[i].team1);
getline(show, getmatches[i].team2);
getline(show, getmatches[i].date);
getline(show, getmatches[i].time);
getline(show, getmatches[i].league);
cout << "the match id: " << getmatches[i].id << endl;
cout << "teams " << getmatches[i].team1 << "vs" << getmatches[i].team2 << endl;
cout << "time of match:" << getmatches[i].time << endl;
cout << "date of match " << getmatches[i].date << endl;
cout << "league:" << getmatches[i].league << endl << endl << endl;
i++;
}
return show.close();
}
int main() {
getmatches();
}
The file I am dealing with has each element in a line, and the problem is that it gives me an extra empty output. I tried to change the counter and condition, but it didn't work.
There is nothing more I can think of. I think it's a basic solution, but I can't figure it out.
Don't use eof() in a loop, the way you are. Define an operator>> for match instead.
There are other issues with your code, too. You have struct match defined twice. Your getmatches[] array has the same name as the function it belongs to, use a more unique name instead. And getmatches() has a void return value, but you are trying to return a (void) value, which is not necessary.
Try this instead:
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct match {
string id;
string team1;
string team2;
string date;
string time;
string league;
};
istream& operator>>(istream &in, match &m) {
getline(in, m.id);
getline(in, m.team1);
getline(in, m.team2);
getline(in, m.date);
getline(in, m.time);
getline(in, m.league);
return in;
}
ostream& operator<<(ostream &out, const match &m) {
out << "the match id: " << m.id << "\n";
out << "teams: " << m.team1 << " vs " << m.team2 << "\n";
out << "time of match: " << m.time << "\n";
out << "date of match: " << m.date << "\n";
out << "league: " << m.league << "\n\n";
return out;
}
void getmatches() {
int i = 0;
const int size = 50;
match matches[size];
ifstream show("matches.txt");
while (i < size && show >> matches[i])
{
cout << matches[i] << endl;
++i;
}
}
int main() {
getmatches();
}
Demo
trying to format with c++ getline function. The output puts everything at the first record number forename instead of where it should go.
Code:
#include <fstream>
#include <string>
#include <iostream>
using namespace std;
int main()
{
const int RANGE = 12;
string tab[RANGE];
int i = 0, j = 0;
ifstream reader("records.txt");
if (!reader)
{
cout << "Error opening input file" << endl;
return -1;
}
while (!reader.eof())
{
if ( ( i + 1) % 4 == 0)
getline( reader, tab[i++], '\n');
else
getline( reader, tab[i++], '\t');
}
reader.close();
i = 0;
while (i < RANGE)
{
cout << endl << "Record Number: " << ++j << endl;
cout << "Forename: " << tab[i++] << endl;
cout << "Surname: " << tab[i++] << endl;
cout << "Department: " << tab[i++] << endl;
cout << "Telephone: " << tab[i++] << endl;
}
return 0;
}
Contents of TXT file:
John Smith Sales 555-1234
Mary Jones Wages 555-9876
Paul Harris Accts 555-4321
Please run the code for yourself to understand what happens and put the txt file in the same folder as your code.
Hope someone can help me thanks.
See Why is iostream::eof inside a loop condition (i.e. while (!stream.eof())) considered wrong?.
Also, your final while loop should only output the strings that were actually read into the array, not the full array, if the file has less than 12 strings. But unless you can guarantee that your file never exceeds 12 strings, you should use std::vector instead of a fixed array.
Also, instead of alternating the getline() delimiter in a single loop, I would just use an outer loop to read whole lines only, and then separately read tab-delimited values from each line. And then store the values in an array/vector of struct instead of individually.
Try something more like this:
#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
#include <vector>
using namespace std;
struct Person
{
string foreName;
string surName;
string department;
string phoneNumber;
};
int main()
{
ifstream reader("records.txt");
if (!reader)
{
cout << "Error opening input file" << endl;
return -1;
}
vector<Person> people;
string line;
while (getline(reader, line))
{
istringstream iss(line);
Person p;
getline(iss, p.foreName, '\t');
getline(iss, p.surName, '\t');
getline(iss, p.department, '\t');
getline(iss, p.phoneNumber, '\t');
people.push_back(p);
}
reader.close();
int j = 0;
for (Person &p : people)
{
cout << endl << "Record Number: " << ++j << endl;
cout << "Forename: " << p.foreName << endl;
cout << "Surname: " << p.surName << endl;
cout << "Department: " << p.department << endl;
cout << "Telephone: " << p.phoneNumber << endl;
}
return 0;
}
There are easier ways to separate words in an istream, namely C++ sring stream tools:
#include <fstream>
#include <iostream>
#include <sstream> //<-- string stream library
using namespace std; //<-- should not be used, use scope std::
int main() {
const int RANGE = 12;
string tab[RANGE];
string temp; //<--to store each field temporarily
int i = 0, j = 0;
ifstream reader("records.txt");
if (!reader) {
cout << "Error opening input file" << endl;
return -1;
}
while (getline(reader, temp)) { //<-- read one full line
stringstream ss(temp); // <-- input to a string stream
while(ss >> tab[i]){ // <-- passing strings to the string array one by one
i++;
}
}
reader.close();
i = 0;
while (i < RANGE) {
cout << endl << "Record Number: " << ++j << endl;
cout << "Forename: " << tab[i++] << endl;
cout << "Surname: " << tab[i++] << endl;
cout << "Department: " << tab[i++] << endl;
cout << "Telephone: " << tab[i++] << endl;
}
return 0;
}
The idea here was to mess as little as possible with your code, one thing I would advise is to use std::vector instead of normal fixed size arrays. Also, as it was said and linked, eof is very unreliable.
The source of your problem, I think, is explained in Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?.
You are reading into tab[12], tab[13], tab[13], and tab[14] due to that error. Of course, that leads to undefined behavior.
Change the loop to:
// Read the contents of the file line by line
std::string line;
while (getline( reader, line))
{
// Process each line's contents.
std::istringstream str(line);
getline(str, tab[i++], '\t');
getline(str, tab[i++], '\t');
getline(str, tab[i++], '\t');
getline(str, tab[i++], '\n');
}
Make sure to add
#include <sstream>
To be doubly sure that you are not using the array using out of bounds indices, add a check.
while ( i+4 < RANGE && getline( reader, line))
{
...
}
First, while (!reader.eof()) is not doing the right thing.
The immediate problem you see is caused by the fact that your file does not contain '\t', hence already the very first getline reads all the contents of the file into tab[0]. (At least thats what I got after 1-to-1 copying your file contents)
Your code is rather difficult, because you declare variables long before you use them and later reuse them. You have a fixed size array, but when there are more lines in the file your code will just crash. Also reading everything into a plain array of strings is making things complicated. Accessing forename or other fields requires you to compute the offset into the array. Better use a data structure:
struct file_entry {
std::string first_name;
std::string last_name;
std::string departure;
std::string phone;
};
Then you can define an input operator:
std::istream& operator>>(std::istream& in,file_entry& fe) {
return in >> fe.first_name >> fe.last_name >> fe.departure >> fe.phone;
};
And use a std::vector to store as many entries as there are in the file:
int main() {
std::string contents{"John Smith Sales 555-1234\n"
"Mary Jones Wages 555-9876\n"
"Paul Harris Accts 555-4321\n"};
std::stringstream reader{contents};
std::vector<file_entry> data;
std::string line;
while (std::getline(reader,line)) {
file_entry fe;
std::stringstream{line} >> fe;
data.push_back(fe);
}
for (const auto& fe : data) {
std::cout << "Forename: " << fe.first_name << '\n';
std::cout << "Surname: " << fe.last_name << '\n';
std::cout << "Department: " << fe.departure << '\n';
std::cout << "Telephone: " << fe.phone << '\n';
}
}
live example
PS you do not need to call close on the file, this is already done in its destructor. Not calling it explicitly has the benefit that the same code that works for a file stream also works for a stringstream.
I declared a vector<string> and I cannot even compile it. I tried many ways but none of them worked.
I'm trying to write out the x.surname.push_back(word)[i] but it's definetly written wrongly and I have no idea how to write it properly and make it possible to compile.
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
int main() {
int number, i = 0;
string word;
struct donators {
vector<string> surname;
vector<int> amount;
} x;
cout << "How many donators do you want to register? " << endl;
cin >> number;
for (i = 0; i < number; i++) {
cout << "Surname: ";
cin >> word;
x.surname.push_back(word)[i];
cout << "Amount: ";
x.amount.push_back(i);
cin >> x.amount[i];
}
cout << "OUR GORGEUS DONATORS: " << endl;
for (i = 0; i < number; i++) {
if (x.amount[i] >= 10000) {
cout << "Surname: " << x.surname(word)[i];
cout << "Amount: " << x.amount[i] << endl;
}
else if (x.amount[i] < 10000) {
cout << "Lack of surnames!" << endl;
}
}
cout << "OUR CASUAL DONATORS: " << endl;
for (i = 0; i < number; i++) {
if (x.amount[i] < 10000) {
cout << "Surname: " << x.surname(word)[i];
cout << "Amount: " << x.amount[i] << endl;
} else if (x.amount[i] >= 10000) {
cout << "Lack of surnames!" << endl;
}
}
return 0;
}
And one more thing. How to make sentence "Lack of surnames!" to be written out once? In some cases, it is written out twice or more times what is redundant.
You are putting [i] at seemingly random places in your code. Such as in x.surname.push_back(word)[i];. Don't add things like this to your code if you're unsure about what they're doing.
The x.surname(word)[i] construct are also wrong. What's x.surname(word) supposed to be? This syntax is for function calls. surname, however, is not a function. It's a std::vector<std::string>. Just put x.surname[i] instead.
And one more thing. How to make sentence "Lack of surnames!" to be
written out once? In some cases, it is written out twice or more times
what is redundant.
That's because you write it for every donor that doesn't fit the criterion. Instead, keep track if any donor fits the criterion and only print it when none ends up fitting. You can do it like this:
bool HasGorgeousDonators = false;
And then in the loop:
if (x.amount[i] >= 10000)
{
cout << "Surname: " << x.surname[i];
cout << "Amount: " << x.amount[i] << endl;
HasGorgeousDonators = true;
}
And after the loop:
if (!HasGorgeousDonators)
cout << "Lack of surnames!" << endl;
Likewise for the other loop. Also, please consider the following Q&A:
Why is "using namespace std;" considered bad practice?
It seems like you are writing C with some C++ help functions. However C++ is a different language. Sure, it supports some C structures, but there's so much more.
Take a look at some of my suggestions for implementation and compare it to your code:
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
template<typename T>
T ReadCin(std::string_view const& sv = "") {
T retVal;
if (!sv.empty()) std::cout << sv;
std::cin >> retVal;
return retVal;
}
class Donator {
private:
std::string surname{};
int amount{};
public:
constexpr bool IsGenerous() const noexcept { return amount >= 10000; }
void Read() noexcept {
surname = ReadCin<decltype(surname)>("Surname: ");
amount = ReadCin<decltype(amount)>("Amount: ");
}
friend std::ostream& operator<<(std::ostream& out, Donator const& donator) noexcept {
out << "Surname: " << donator.surname << ", " << "Amount: " << donator.amount;
return out;
}
};
int main() {
std::vector<Donator> donators(ReadCin<int>("How many donators do you want to register?\n"));
for (auto& donator : donators) donator.Read();
std::cout << "OUR GENEROUS DONATORS:\n";
std::copy_if(std::cbegin(donators), std::cend(donators), std::ostream_iterator<Donator>(std::cout, "\n"),
[](Donator const& donator) { return donator.IsGenerous(); });
std::cout << "OUR CASUAL DONATORS:\n";
for (auto const& donator : donators) if (!donator.IsGenerous()) std::cout << donator << '\n'; //alternative
}
I tried to include some of the possibilities using C++. I would really advise you to get a good book on C++.
I have this (I've just started to learn btw):
#include <iostream>
#include <string>
using namespace std;
int main()
{
string mystr;
cout << "Welcome, what is your name? ";
getline(cin, mystr);
cout << "Nice to meet you, " << mystr << endl;
cout << "May i call you 1 for short? (y/n)" << endl;
getline(cin, mystr);
}
I want to next say;
cout << "Thank you, 1" << endl;
OR:
cout << "Well ok, " << mystr << endl;
... based on whether or not the user has typed y or n. How would i go about this? I've had a look around but i don't really know how to word it. I'm using Visual Studio Express and it is a console application.
For a very simple way:
if (mystr == "1") {
// ...
}
But you should accustom yourself to more error checking, so check the state of the stream after getline:
getline(cin, mystr);
if (cin) {
if (mystr == "1") {
// ...
}
} else {
// error
}
And of course, you may want to support any number in the future, not just 1. Then you need to convert the input string to a number. See std::stoi if you use C++11, or look at the thousands of past Stackoverflow questions about string-to-number conversions :)
Edit: Just noticed that you actually wanted to check for "y". Well, that's the same then:
if (mystr == "y") {
// ...
}
You should use if-else statement. For example
#include <cctype>
//...
std::string name = mystr;
std::cout << "May i call you 1 for short? (y/n)" << std::endl;
std::getline( std::cin, mystr );
for ( char &c : mystr ) c = std::tolower( c );
if ( mystr == "y" )
{
name = "1";
std::cout << "Thank you, " << name << std::endl;
}
else
{
std::cout << "Well ok, " << name << std::endl;
}
I am trying to load a text file and import the contents into a vector of structs.
Here are my definitions
typedef struct
{
string pcName, pcUsername, pcPassword, pcMessage, pcAdvertisement; //I know that
//this is incorrect convention. It was originally a char*
}
ENTRY;
vector<ENTRY> entries;
fstream data;
Here is my display data function
void DisplayData()
{
std::cout << (int)(entries.size() / 5) <<" entries" << endl;
for(int i = 1; i <=(int)entries.size()/5; i++)
{
cout << endl << "Entry " << i << ":" << endl
<< "Name: " << entries[i].pcName << endl
<< "Username: " << entries[i].pcUsername << endl
<< "Password: " << entries[i].pcPassword << endl
<< "Message: " << entries[i].pcMessage << endl
<< "Advertisement: " << entries[i].pcAdvertisement << endl;
}
}
and here is my Load Data function
bool LoadData(const char* filepath)
{
std::string lineData ;
int linenumber = 1 ;
data.open(filepath, ios::in);
ENTRY entry_temp;
if(!data.is_open())
{
cerr << "Error loading file" << endl;
return false;
}
while(getline(data, lineData))
{
if(linenumber==1) {entry_temp.pcName = lineData;}
else if(linenumber==2) {entry_temp.pcUsername = lineData;}
else if(linenumber==3) {entry_temp.pcPassword = lineData;}
else if(linenumber==4) {entry_temp.pcMessage = lineData;}
else if(linenumber==5) {entry_temp.pcAdvertisement = lineData;}
entries.push_back(entry_temp);
if(linenumber == 5)
{
linenumber = 0;
}
linenumber++;
}
data.close();
puts("Database Loaded");
return true;
}
Here is the text file I am loading:
Name1
Username1
Password1
Message1
Ad1
And here is the result of the display data function after calling load data:
1 entries
Entry 1:
Name: Name1
Username Username1
Password:
Message:
Advertisement:
As you can see, the first two load but the last three don't. When I did this with an array instead of a vector, it worked fine so I don't know what I'm doing wrong. Thanks.
I suggest that you read each line directly into the data field where it goes:
getline(data, entry_temp.pcName);
getline(data, entry_temp.pcUsername);
getline(data, entry_temp.pcPassword);
getline(data, entry_temp.pcMessage);
getline(data, entry_temp.pcAdvertisement);
entries.push_back(entry_temp);
This makes your intent much clearer than your current while loop. It also creates a single entry for all 4 input lines rather than one for each input line (with the other three blank). Now you can read several "entries" by using a while loop that checks if you have reached the end of the file.
Doing this will also make printing out the data much easier since the vector will have exactly the number of entries rather than five times as many as you expect (which also eats up a lot more memory than you need to).
Your DisplayData function is a little weird, and so is your LoadData.
Your LoadData pushes back a new copy of the current ENTRIES entry with every line. Your DisplayData starts at 1 (which is not the beginning of any vector or array), and iterates only up to the 1/5th entry of the entire vector.
This needs a heavy rework.
First, the size() member of any standard container returns the number of elements that it contains, and will not take the number of fields in a contained struct into account.
For future reference, you'll want to post your question in a complete, standalone example that we can immediately compile to help. (see http://sscce.org/)
Try this modified data, which runs correctly, and see if you can tell what is being done differently:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
typedef struct
{
string pcName, pcUsername, pcPassword, pcMessage, pcAdvertisement;
}
ENTRY;
vector<ENTRY> entries;
fstream data;
bool LoadData(const char* filepath)
{
std::string lineData ;
int linenumber = 1 ;
data.open(filepath, ios::in);
ENTRY entry_temp;
if(!data.is_open())
{
cerr << "Error loading file" << endl;
return false;
}
while(getline(data, lineData))
{
if(linenumber==1) {entry_temp.pcName = lineData;}
else if(linenumber==2) {entry_temp.pcUsername = lineData;}
else if(linenumber==3) {entry_temp.pcPassword = lineData;}
else if(linenumber==4) {entry_temp.pcMessage = lineData;}
else if(linenumber==5) {entry_temp.pcAdvertisement = lineData;}
if(linenumber == 5)
{
entries.push_back(entry_temp);
linenumber = 0;
}
linenumber++;
}
data.close();
puts("Database Loaded");
return true;
}
void DisplayData()
{
std::cout << entries.size() <<" entries" << endl;
for(int i = 0; i < entries.size(); i++)
{
cout << endl << "Entry " << i << ":" << endl
<< "Name: " << entries[i].pcName << endl
<< "Username: " << entries[i].pcUsername << endl
<< "Password: " << entries[i].pcPassword << endl
<< "Message: " << entries[i].pcMessage << endl
<< "Advertisement: " << entries[i].pcAdvertisement << endl;
}
}
int main()
{
LoadData("/tmp/testdata");
DisplayData();
return (0);
}
While I think #code-guru has the right idea, I'd take the same idea just a little further, and make your code work a little more closely with the standard library. I'd do that by reading a data item with a stream extractor, and displaying it with stream inserter. So, the extractor would look something like this:
std::istream &operator>>(std::istream &is, ENTRY &e) {
getline(is, e.pcName);
getline(is, e.pcUsername);
getline(is, e.pcPassword);
getline(is, e.pcMessage);
getline(is, e.pcAdvertisement);
return is;
}
..and the inserter would look something like this:
std::ostream &operator<<(std::ostream &os, ENTRY const &e) {
os << e.pcName << "\n";
os << e.pcUsername << "\n";
os << e.pcPassword << "\n";
os << e.pcMessage << "\n";
os << e.pcAdvertisement << "\n";
return os;
}
With those in place, loading and displaying the data becomes fairly straightforward.
Load the data:
std::ifstream in("yourfile.txt");
std::vector<ENTRY> data((std::istream_iterator<ENTRY>(in)),
std::istream_iterator<ENTRY>());
Display the data:
for (auto const & e: data)
std::cout << e << "\n";
For the moment, I haven't tried to duplicate the format you were using to display the data -- presumably the modifications for that should be fairly obvious.