Arithmetic from an input file to an output file? - c++

This is a two part question and I hope I make myself understood. I'll edit as necessary! I'm attempting to write a program that will do calculations in succession from an input file. The text file will look something like this:
int + 25 10
double / 5.5 8.5
...
Each instance starts with a type like int, double, float, etc., then a calculation type (add, subtract, etc.) and then two numbers. I want to be able to read each in succession and output the sum, product, etc. into an output file. For instance, if we use the first example above, the output in the file will be:
int 25 10 = 35
I have code which will do the calculations below:
void doAddition(ifstream &inFile) {
int num1, num2;
inFile >> num1 >> num2;
cout << num1 << num2 << " = "<< (num1+num2) << '\n'; }
The only problem with this is that I don't know how to add the type of variable (I've tried using string but it doesn't seem to work), for instance "int" or "double" so I get:
25 10 = 35
Instead of:
int 25 10 = 35
My second problem as you may see is that I'm currently using "cout" to show the information on screen when I really want to add it to the outfile. Here's some more info:
What I use to move to the next line:
void readToNextLine(ifstream& inFile) {
string t1, t2;
inFile >> t1 >> t2; }
Code in my Main:
ifstream inFile;
//ofstream outFile;
char ch;
int num1, num2;
inFile.open("infile.txt");
//outFile.open("outfile.txt");
if (inFile.is_open()){
inFile >> ch;
while (!inFile.eof())
{
switch (ch)
{
case '+':
doAddition(inFile);
break;
...
As you can see I commented out the ofstream part because I couldn't get it to work properly. Any suggestions? I have about 10 windows and two C++ books open right now just trying to put it all together logically!
Edit: I'm not sure if switches would be the best method for this. I need the program to see "int" and realize it's a word. If I used 4 variable types like int, double, float, and long perhaps I could have it check the first letter of each: i, d, f, l and then once it knows what type it could go into the +, -, etc. checks. It feels like by doing this logically it's just taking more time when I could be using a series of classes, but I'm just not sure where to start with that.

I'm really not understanding all this trouble reading from files. There are too many examples on Stackoverflow and the web. Perhaps it's people not searching or they are demanding an example that matches their exact code.
Try this:
struct Input_Record
{
std::string data_type_as_string;
std::string operation;
std::string value1_as_string;
std::string value2_as_string;
friend std::istream& operator>>(std::istream& inp, Input_Record& r);
};
std::istream& operator>>(std::istream& inp, Input_Record& r)
{
inp >> r.data_type_as_string;
inp >> r.operation;
inp >> r.value1_as_string;
std::getline(inp, r.value2_as_string); // Use getline to eat the line ending.
}
// ...
Input_Record r;
while (input_file >> r)
{
// Do stuff with r
};
If you really want to have some fun, You could use a parent base class and a factory pattern to generically create objects based on the input:
class Binary_Operation // Base class for factory pattern.
{
public:
//! Evaluate the object and return the result as a string
// e.g. numbers as text
virtual std::string evaluate(void) const = 0;
};
class Binary_Integer_Operation : public Binary_Operation
{
public:
std::string evaluate(void) const
{
// Convert values to integers than perform the operation.
// Convert result to string using std::istringstream.
};
};
class Binary_Double_Operation : public Binary_Operation
{
// See Binary_Integer_Operation above.
};
This allows you to do something like:
Binary_Operation * factory_create(const Input_Record& r)
{
Binary_Operation * p_obj = nullptr;
if (r.data_type == "int")
{
p_obj = new Binary_Integer_Operation;
// Initialize fields
}
if (r.data_type == "double")
{
p_obj = new Binary_Double_Operation;
// Initialize fields;
}
return p_obj;
}
Your processing loop would look like:
Input_Record r;
while (input_file >> r)
{
Binary_Operation * p_operation = factory_create(r);
std::string result = p_operation->evaluate();
cout << "result = " << result << "\n";
delete p_operation;
}

Let's start with an example like the one you provided:
int + 25 10
The type for the "type" and arithmetic operator is simple, std::string and char respectively.
std::ifstream in("infile.txt");
std::string type; char op;
if (in >> type >> op)
{
// ...
}
For the other two values, you also have to extract them into strings because you first have to find out the value of type before you can convert them:
if (in >> type >> op >> a >> b) // a and b are strings
Now use a function to check type and convert a and b to the correct types:
void convertTo(std::string const& typeName, std::string const& a, std::string const& b, char op)
{
if (typeName == "int")
{
int a1 = std::stoi(a),
b2 = std::stoi(b);
doOperation(op, a1, b2)
} else if (typeName == "double") {
double a1 = std::stod(a),
b2 = std::stod(b);
doOperation(op, a1, b2);
} else if (typeName == "float") {
// std::stof()
}
}
doOperation() is templated and is implemented like this:
template<typename T>
struct F;
template<> struct F<int> { static const std::string value = "int"; };
template<> struct F<double> { static const std::string value = "double"; };
template<> struct F<float> { static const std::string value = "float"; };
template<typename U, std::string name = F<U>::value>
void doOperation(char op, U a, U b)
{
std::ofstream out("outfile.txt");
switch (op)
{
case '+':
out << name << " " << op << " " << (a + b);
case '-':
out << name << " " << op << " " << (a - b);
case '/':
// ...
// ...
}
}

Related

C++ accessing vectors in classes

i am a beginner in C++ and my question is:
why my vector in a class is empty when i try to access that vector elements in another class after i added elements to that vector?
i have a class for example class1 and this class has a vector of type string and a member function which adds elements to the vector with push_back() and another member function which has an argument of type string and it returns true if the argument is in the vector or else it returns false. now if i write another class class2 and it has a vector of type string named valid and a member function named check that it reads a string from input and we have a class1 object that we can access the class1 member function to check if this input is in the vector from class1 but looks like in class2 the vector i had in class1 with elements is empty. what am i doing wrong?
here is code:
class abc{
private:
vector<string> words;
public:
void seta() {
string s;
cout << "word: ";
cin >> s;
words.push_back(s);
}
bool word_check(string a) {
for(string b : words) {
if(b == a) {
return true;
}
}
return false;
}
};
class b{
private:
vector<string> valid;
public:
void check() {
abc mlj;
string k;
cout << "Enter word to check: ";
cin >> k;
bool w = mlj.word_check(k);
while(w == false) {
cerr << "invalid input, try again: ";
cin.clear();
cin.ignore(INT_MAX, '\n');
cin >> k;
}
valid.push_back(k);
}
};
int main() {
abc vkk;
vkk.seta();
vkk.seta();
vkk.seta();
b pla;
pla.check();
}
screenshot of the output
i was expecting that i can access vector elements in class from another class
mlj is a new local object in the check method, and it contains no words. All your words were input in the main function and are stored in vkk. So you need to pass that object to check.
To do that, modify the method to receive a reference
void check(const abc & mlj)
{
string k;
cout << "Enter word to check: ";
cin >> k;
bool w = mlj.word_check(k);
// ...
valid.push_back(k);
}
Now, this will give you a compiler error, because abc::word_check is a non-const method. Let's also fix that by adding the const specifier to the method definition. While we're at it, let's accept the string as a const reference too, and also use references while iterating over the vector. This avoids unnecessary string copying.
bool word_check(const string& a) const
{
for(const string& b : words) {
if(b == a) {
return true;
}
}
return false;
}
It should be noted that this can also be achieved with std::find which is provided by the standard library in <algorithm>:
bool word_check(const string& a) const
{
return std::find(words.begin(), words.end(), a) != words.end();
}
Let's circle back to your main, and update that to call check correctly:
int main() {
abc vkk;
vkk.seta();
vkk.seta();
vkk.seta();
b pla;
pla.check(vkk); // <-- pass vkk here
}
One other thing to note is your loop in check is broken. If w is false, then the loop will never terminate because you never update w again. How about instead you do this:
while ((cin >> k) && !mlj.word_check(k))
{
cerr << "invalid input, try again: ";
cin.ignore(INT_MAX, '\n');
cin >> k;
}
if (cin)
{
valid.push_back(k);
}
This does a couple of things at once...
First, it ensures the stream has actually read a string and not entered some error state (such as end of stream). Under ordinary conditions, reading strings from standard input will not result in error bits being set, so you also don't need cin.clear().
Second, it calls word_check every time around the loop, and only enters the loop body if the check fails. After the loop, we test once again that the stream is good, and if so then it means we read a word and it passed the check.
Make these changes, and you're at least on the way to having a working program. There are other nit-picks I could make, but I may have done too many already so I'll stop! Happy coding!
In the code that you have given as an example, you have created two separate objects of the same class, each of which occupies a different space in memory and is completely independent of each other. Therefore, the mlj object is completely independent from the vkk object and nothing has been inserted in it so far. For your code to work properly I suggest you make the following change to it. That is, give the class abc to the input of class b:
class abc {
private:
vector<string> words;
public:
void seta() {
string s;
cout << "word: ";
cin >> s;
words.push_back(s);
}
bool word_check(string a) {
for (string b : words) {
if (b == a) {
return true;
}
}
return false;
}
};
class b {
private:
vector<string> valid;
public:
void check(abc mlj) {
string k;
cout << "Enter word to check: ";
cin >> k;
bool w = mlj.word_check(k);
while (w == false) {
cerr << "invalid input, try again: ";
cin.clear();
cin.ignore(INT_MAX, '\n');
cin >> k;
}
valid.push_back(k);
}
};
int main() {
abc vkk;
vkk.seta();
vkk.seta();
vkk.seta();
b pla;
pla.check(vkk);
}

how can i read file line by line for c++

beginner in c++ here...
im making a multiple txt file to being read, it is testAnswers and CorrectAnswers
inside testAnswers is:
S1234 A B D C C C D A D D
S2345 A C D A A B D C A A
S3456 C A A A C C D D B B
S4567 A B B B B A B C C D
S5678 B C C B A A B C C D
S6789 A B B C C A B A C C
S7890 C B A A D A C A A C
S8912 A D A D A C C B A C
S9123 B C C A A C D B B A
S1928 B C C A A B A D B B
S2938 A B D C B D B D A A
S3847 C B D C C C B D A B
inside CorrectAnswers is:
A C D A A B B D C D
here the code i do
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main(int argc, char** argv) {
ifstream inputStream1, inputStream2;
ofstream outputStream;
inputStream1.open("testAnswers.txt");
inputStream2.open("CorrectAnswers.txt");
outputStream.open("outputAnswers.txt");
string name, a1, b1, c1, d1, e1, f1, g1, h1, i1, j1;
string a2, b2, c2, d2, e2, f2, g2, h2, i2, j2;
string id;
while(inputStream1 >> name >> a1 >> b1 >> c1 >> d1 >> e1 >> f1 >> g1 >> h1 >> i1 >> j1){
}
while(inputStream2 >> a2 >> b2 >> c2 >> d2 >> e2 >> f2 >> g2 >> h2 >> i2 >> j2){
}
cout << "id: ";
cin >> id;
if(id == name){
if(a1==a2){
num+=1;
}
if(b1==b2){
num+=1;
}
if(c1==c2){
num+=1;
}
if(d1==d2){
num+=1;
}
if(e1==e2){
num+=1;
}
if(f1==f2){
num+=1;
}
if(g1==g2){
num+=1;
}
if(h1==h2){
num+=1;
}
if(i1==i2){
num+=1;
}
if(j1==j2){
num+=1;
}
}
cout << num << endl;
outputStream << num << endl;
inputStream1.close();
inputStream2.close();
outputStream.close();
return 0;
}
ABCD is an answer. example, User S1234 is answers A B D C C C D A D D. if we compared CorrectAnswers, he got 3 correct
thats what it should be happen. but the code only can read the last User only, S3847. im using while(input) because im assume it will read line by line but it not.
how can i make the code read each line from above first?
Let me try to help you.
First we will look at your program and comment on it.
using namespace std;
you should not open the complete standard namespace. You will find many many comments here on SO recommending that. So, please omit that line an use qualified names everywhere (like std::string)
ifstream inputStream1;
inputStream1.open("testAnswers.txt");
You can and should do this in one line. The iostreams have a constructor, taking the filename. Additionally, you should always check the result of any IO operation. This you can simply do with an if (inputStream1), because the bool and the !-operator for iostreams are overloaded and return the state of stream. And, you should not use hardcoded string literals, please use const std::strings instead and store the file name. Last, but not least, we will use the if-statement with initializer to prevent the pollution of the outer name space. You should always try to narrow the scopy of your variable as much as possible. The resulting statement for opening a file will be:
if (std::ifstream correctAnswersStream{ fileNameCorrectAnswers }; correctAnswersStream) {
Another big advantage is that the iostreams destructor will close the file automatically for you, when they "fall" out of scope. So, no need for a close-statement.
Then you define tons of string variables. First strong recommendation: Please always initialize all variables. Use the {} for default initialization. Ant, if you have many variables with the same meaning, you should put them in a ```std::vector. If you have variables that logically belong together, the put them in a struct(class````). for example like:
std::vector<std::string> answer{};
of for a struct something like that:
struct Test {
std::string name{};
std::vector<std::string> answer{};
};
And then you would define methods to operate on your data. At least you could overwrite the inserter (<<) and the extractor (>>) operator for simple io operations. Only the class (struc) should know, how to read write data, not the outer world.
So you could for example add a simple inserter like the belo:
struct Correct {
std::vector<std::string> answer{};
// Overwrite inserter. Show all data
friend std::ostream& operator << (std::ostream& os, const Correct& c) {
std::copy(c.answer.begin(), c.answer.end(), std::ostream_iterator<std::string>(os, " "));
return os;
}
};
Now let's look at you next statement and the main source of your problems:
while(inputStream1 >> name >> a1 >> b1 >> c1 >> d1 >> e1 >> f1 >> g1 >> h1 >> i1 >> j1){
}
You use a while-loop to read you variables, but, the problem is that you do nothing in the loop body. YOu read the variables, but not store their value in a std::vector or somehwere else. And then, during the next loop run, all preiviously read values will be overwritten with the new vales and their old content will be lost. And when the loop finishes, you will have the values of the last line in your varaibles, because you have overwritten and discarded all previously read values. That needs to changed.
Store the contents of the variables somewhere.
This you need to do for both input files. Then you can compare verything with everything else.
You will find out that you need somehow a 2 dimensional container. Or, a std::vector for your own struct which again contains another std::vector.
Because I want to help you, but not do your homework, I will add an example program using a little bit more sophisticated coding style. With that you can understand the logic, but most certainly not some of the more advanced stuff.
However, of course the following will work:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
struct Correct {
std::vector<std::string> answer{};
friend std::istream& operator >> (std::istream& is, Correct& c) {
// Read a complete line and check, if that worked
if (std::string line{}; std::getline(is, line)) {
// Put line into an istringstream for easier extraction
std::istringstream iss(line);
// CLear all old data from vector
c.answer.clear();
// Copy all answers from file into internal vector
std::copy(std::istream_iterator<std::string>(iss), {}, std::back_inserter(c.answer));
}
return is;
}
// Overwrite inserter. Show all data
friend std::ostream& operator << (std::ostream& os, const Correct& c) {
std::copy(c.answer.begin(), c.answer.end(), std::ostream_iterator<std::string>(os, " "));
return os;
}
};
struct Test {
std::string name{};
std::vector<std::string> answer{};
friend std::istream& operator >> (std::istream& is, Test& t) {
// Read a complete line and check, if that worked
if (std::string line{}; std::getline(is, line)) {
// Put line into an istringstream for easier extraction
std::istringstream iss(line);
// Try to extract the name
if (iss >> t.name) {
// Clear all old data from vector
t.answer.clear();
// Copy all answers from file into internal vector
std::copy(std::istream_iterator<std::string>(iss), {}, std::back_inserter(t.answer));
}
}
return is;
}
// Overwrite inserter. Show all data
friend std::ostream& operator << (std::ostream& os, const Test& t) {
std::copy(t.answer.begin(), t.answer.end(), std::ostream_iterator<std::string>(os, " "));
return os;
}
};
const std::string fileNameTestAnswers{ "r:\\testAnswers.txt" };
const std::string fileNameCorrectAnswers{ "r:\\CorrectAnswers.txt" };
const std::string fileNameResult{ "r:\\outputAnswers.txt" };
int main() {
// Open file with test answers and check, if it could be opened
if (std::ifstream testAnswersStream{ fileNameTestAnswers }; testAnswersStream) {
// Open file with correct answers and check, if it could be opened
if (std::ifstream correctAnswersStream{ fileNameCorrectAnswers }; correctAnswersStream) {
// Open file for the resulting output
if (std::ifstream resultAnswersStream{ fileNameCorrectAnswers }; resultAnswersStream) {
// Now all the files are open and we can start. Try to read all files
std::vector<Correct> correct{};
std::vector<Test> test{};
// Read files
std::copy(std::istream_iterator<Correct>(correctAnswersStream), {}, std::back_inserter(correct));
std::copy(std::istream_iterator<Test>(testAnswersStream), {}, std::back_inserter(test));
// Inform user that he has to enter a name
std::cout << "\nPlease enter a name for checking the results:\n";
// Read the name
if (std::string searchName{}; std::getline(std::cin, searchName)) {
// Search the input search name in our test vector
// Goto all tests, because maybe the name is in the file several times
for (auto record{ std::find_if(test.begin(), test.end(), [&searchName](const Test& t) { return t.name == searchName; }) };
record != test.end();
record = std::find_if(std::next(record), test.end(), [&searchName](const Test& t) { return t.name == searchName; })) {
// So go through all the records that contain the search names and compare with all answers
for (const auto& co : correct) {
size_t okCounter{};
for (size_t r{}, c{}; r < record->answer.size() && c < co.answer.size(); ++r, ++c) {
if (record->answer[r] == co.answer[c]) ++okCounter;
}
// Incase there was a match, show result
if (okCounter > 0U)
std::cout << "\nName:\t\t " << searchName << "\nGiven answer:\t " << *record << "\nCorrect answer:\t "
<< co << "\nNumber matches:\t " << okCounter;
std::cout << "\n\n";
}
}
}
else std::cerr << "\n\nError while reading search name files\n\n";
}
else std::cerr << "\n\nError: Could not open file '" << fileNameResult << "'\n\n";
}
else std::cerr << "\n\nError: Could not open file '" << fileNameCorrectAnswers << "'\n\n";
}
else std::cerr << "\n\nError: Could not open file '" << fileNameTestAnswers << "'\n\n";
return 0;
}

how can I input a literal string into istream? [duplicate]

How to achieve scanf("%d # %d",&a,&b);sort of effect with cin in C++ ?
You can skip the # by extracting it into a character:
std::istringstream iss("10 # 20");
int main()
{
int a, b; char hash;
iss >> a >> hash >> b;
assert(a == 10 && b == 20);
}
You could create your own stream manipulator. It is fairly easy.
#include <ios>
#include <iostream>
using namespace std;
// skips the number of characters equal to the length of given text
// does not check whether the skipped characters are the same as it
struct skip
{
const char * text;
skip(const char * text) : text(text) {}
};
std::istream & operator >> (std::istream & stream, const skip & x)
{
ios_base::fmtflags f = stream.flags();
stream >> noskipws;
char c;
const char * text = x.text;
while (stream && *text++)
stream >> c;
stream.flags(f);
return stream;
}
int main()
{
int a, b;
cin >> a >> skip(" # ") >> b;
cout << a << ", " << b << endl;
return 0;
}
There isn't a direct function inside the istream class that mimics it, unfortunately. There are functions that you might be able to use to manipulate the stream and get the correct input, but I'm not familiar with how they work so I couldn't tell you how.
My best suggestion on how I would personally do it is to use getline() to put the input into a string and then from there I would do a few checks to see if it matches the format. So in your case I would grab the first substring up until the first space, make sure it's a valid decimal, check to make sure the pound sign ('#') is in the correct spot, then grab the ending number to make sure it's valid. If any one of those three objects are incorrect I would set some boolean variable to false to kick out or return or something to indicate that the input was invalid and not the correct format.
Pseudo Code:
...
getline(cin,myStr);
while(!formatCheck(myStr))
{
cout<<"Not valid format for input";
getline(cin,myStr);
}
...
bool formatCheck(string str)
{
string firstPart=str.subString(0,firstSpaceLocation);
string middle=str[firstSpaceLocation+1];
string lastPart=str.subString(firstSpaceLocation+3,end);
if(first part not a valid number || middle!="#" || last part not a valid number)
{
return false;
}
return true;
}
Here's another way. You can classify # as a whitespace character through the std::ctype<char> facet imbued in the locale:
#include <iostream>
#include <sstream>
#include <vector>
namespace detail
{
enum options { add, remove };
class ctype : public std::ctype<char>
{
private:
static mask* get_table(const std::string& ws, options opt)
{
static std::vector<mask> table(classic_table(),
classic_table() + table_size);
for (char c : ws)
{
if (opt == add)
table[c] |= space;
else if (opt == remove)
table[c] &= ~space;
}
return &table[0];
}
public:
ctype(const std::string& ws, options opt)
: std::ctype<char>(get_table(ws, opt)) { }
};
}
class adjustws_impl
{
public:
adjustws_impl(const std::string& ws, detail::options opt) :
m_ws(ws),
m_opt(opt)
{ }
friend std::istream& operator>>(std::istream& is,
const adjustws_impl& manip)
{
is.imbue(std::locale(is.getloc(),
new detail::ctype(manip.m_ws, manip.m_opt)));
return is;
}
private:
std::string m_ws;
detail::options m_opt;
};
adjustws_impl setws(const std::string& ws)
{
return adjustws_impl(ws, detail::add);
}
adjustws_impl unsetws(const std::string& ws)
{
return adjustws_impl(ws, detail::remove);
}
int main()
{
std::istringstream iss("10 # 20");
int a, b;
iss >> setws("#");
iss >> a >> b;
iss >> unsetws("#");
std::cout << a << ' ' << b; // 10 20
}
You can skip the #, or any single character, by using std::istream::ignore
std::istringstream sstr("1024 # 768");
int main()
{
int a, b;
sstr >> a;
sstr.ignore(256,'#'); // ignore until hash character
sstr >> b;
std::cout << "a: " << a << " b: " << b << std::endl;
}

Skipping expected characters like scanf() with cin

How to achieve scanf("%d # %d",&a,&b);sort of effect with cin in C++ ?
You can skip the # by extracting it into a character:
std::istringstream iss("10 # 20");
int main()
{
int a, b; char hash;
iss >> a >> hash >> b;
assert(a == 10 && b == 20);
}
You could create your own stream manipulator. It is fairly easy.
#include <ios>
#include <iostream>
using namespace std;
// skips the number of characters equal to the length of given text
// does not check whether the skipped characters are the same as it
struct skip
{
const char * text;
skip(const char * text) : text(text) {}
};
std::istream & operator >> (std::istream & stream, const skip & x)
{
ios_base::fmtflags f = stream.flags();
stream >> noskipws;
char c;
const char * text = x.text;
while (stream && *text++)
stream >> c;
stream.flags(f);
return stream;
}
int main()
{
int a, b;
cin >> a >> skip(" # ") >> b;
cout << a << ", " << b << endl;
return 0;
}
There isn't a direct function inside the istream class that mimics it, unfortunately. There are functions that you might be able to use to manipulate the stream and get the correct input, but I'm not familiar with how they work so I couldn't tell you how.
My best suggestion on how I would personally do it is to use getline() to put the input into a string and then from there I would do a few checks to see if it matches the format. So in your case I would grab the first substring up until the first space, make sure it's a valid decimal, check to make sure the pound sign ('#') is in the correct spot, then grab the ending number to make sure it's valid. If any one of those three objects are incorrect I would set some boolean variable to false to kick out or return or something to indicate that the input was invalid and not the correct format.
Pseudo Code:
...
getline(cin,myStr);
while(!formatCheck(myStr))
{
cout<<"Not valid format for input";
getline(cin,myStr);
}
...
bool formatCheck(string str)
{
string firstPart=str.subString(0,firstSpaceLocation);
string middle=str[firstSpaceLocation+1];
string lastPart=str.subString(firstSpaceLocation+3,end);
if(first part not a valid number || middle!="#" || last part not a valid number)
{
return false;
}
return true;
}
Here's another way. You can classify # as a whitespace character through the std::ctype<char> facet imbued in the locale:
#include <iostream>
#include <sstream>
#include <vector>
namespace detail
{
enum options { add, remove };
class ctype : public std::ctype<char>
{
private:
static mask* get_table(const std::string& ws, options opt)
{
static std::vector<mask> table(classic_table(),
classic_table() + table_size);
for (char c : ws)
{
if (opt == add)
table[c] |= space;
else if (opt == remove)
table[c] &= ~space;
}
return &table[0];
}
public:
ctype(const std::string& ws, options opt)
: std::ctype<char>(get_table(ws, opt)) { }
};
}
class adjustws_impl
{
public:
adjustws_impl(const std::string& ws, detail::options opt) :
m_ws(ws),
m_opt(opt)
{ }
friend std::istream& operator>>(std::istream& is,
const adjustws_impl& manip)
{
is.imbue(std::locale(is.getloc(),
new detail::ctype(manip.m_ws, manip.m_opt)));
return is;
}
private:
std::string m_ws;
detail::options m_opt;
};
adjustws_impl setws(const std::string& ws)
{
return adjustws_impl(ws, detail::add);
}
adjustws_impl unsetws(const std::string& ws)
{
return adjustws_impl(ws, detail::remove);
}
int main()
{
std::istringstream iss("10 # 20");
int a, b;
iss >> setws("#");
iss >> a >> b;
iss >> unsetws("#");
std::cout << a << ' ' << b; // 10 20
}
You can skip the #, or any single character, by using std::istream::ignore
std::istringstream sstr("1024 # 768");
int main()
{
int a, b;
sstr >> a;
sstr.ignore(256,'#'); // ignore until hash character
sstr >> b;
std::cout << "a: " << a << " b: " << b << std::endl;
}

getting selected data from a vector then storing as a variable

Ive currently stored all my data into a vector as variables. im trying to get selected data from that vector, storing then as variables so i can then do calculations, save the answers as a variable to then store back in my original vector?
The data file is in the format below;
a b c d e
1 7.3 0.8 14 74.6
2 6.5 0.1 13 3.3
3 10.8 1.4 12 75.8
4 13.2 3.5 6 32.4
my code is as below so far;
struct Weather
{
int a_data;
double b_data;
double c_data;
int d_data;
double e_data;
double ans_data;
};
int main ()
{
using std::vector;
using std::string;
using std::getline;
using std::cout;
vector<Weather> data_weather;
string line;
ifstream myfile ("weatherdata.txt");
if (myfile.is_open())
{
int count = 0;
while (getline(myfile, line))
{
if (count > 6)
{
int a, d;
double b, c, e;
std::istringstream buffer(line);
std::string sun_as_string;
if (buffer >> a >> b >> c >> d >>e_as_string)
{
if (e_as_string == "---")
{
e = 0.0;
}
else
{
std::istringstream buffer2(e_as_string);
if (!(buffer2 >> e))
{
e = 0.0;
}
}
Weather objName = {a, b, c, d, e};
data_weather.push_back(objName);
}
}
count++;
}
myfile.close();
double temp_b, temp_c, temp_ans; //declaring my new variables
for (auto it = data_weather.begin(); it != data_weather.end(); ++it)
{
std::cout << it->b_data << " " << it->c_data << std::endl;
}
}
}
else
cout << "unable to open file";
scat::pause("\nPress <ENTER> to end the program.");
return 0;
}
any help would be appreciated
Am missing something obvious or do you simply need to do this?
for (auto it = data_weather.begin(); it != data_weather.end(); ++it)
{
it->ans_data = it->b_data * it->c_data;
}
Dereferencing an iterator gives you a reference to an existing object inside the vector. You don't really need temporary variables for this.
A nicer C++11 alternative is a range based for loop:
for (Weather& w : data_weather)
{
w.ans_data = w.b_data * w.c_data;
}
Given a list of indices of lines you want to work with, you can do something like this:
Weather& w = data_weather[i]; // shortcut so you don't need to
// write data_waether[i] each time
w.ans_data = (w.b_data * w.c_data)/2;
where i is the index of the line your interested in. You might want to put this is some kind of a loop. I leave that as an exercise for you :)
I would structure this somewhat differently. I'd write the code for reading the data as an operator>> for the Weather structure:
std::istream &operator>>(std::istream &is, Weather &w) {
std::string line;
std::getline(is, line);
std::istringstream buffer(line);
buffer >> w.a >> w.b >> w.c >> w.d;
if (!buffer >> w.e) // conversion will fail for "---" or not-present
w.e = 0.0;
return is;
}
This can simplify reading the data:
std::ifstream myfile("Weather.txt");
std::string ign;
std::getline(myfile, ign); // skip that pesky first line
// Now we can initialize the vector directly from the data in the file:
std::vector<Weather> weather_data((std::istream_iterator<Weather>(myfile)),
std::istream_iterator<Weather>());
With a reasonably recent compiler, printing out the data can be simplified as well:
for (auto &w : weather_data)
std::cout << w.b_data << " " << w.c_data << "\n";
If you want to do a calculation, and put a result back into the ans field of your structure, that's fairly easy also. For example, let's assume b, c and d are temperatures, and we want ans to contain the average of the three:
for (auto &w : weather_data)
w.ans = (w.b_data + w.c_data + w.d_data)/3.0;