I am altering a (well, my first) C++ program that evaluates expressions. For example, by inputting this:
(x + (y * 3)), x = 2, y = 6;
It should output the following:
Value = 20
The program works when the user types the expression into the console, but I want it to read and evaluate expressions from a text file (each on a separate line). I have successfully read the file in, and can even print its contents in the console, but I'm having trouble parsing and evaluating each line. There are many more header and source files that accompany this project, but I think all of the relevant information is in the main function:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
#include "expression.h"
#include "subexpression.h"
#include "symboltable.h"
#include "parse.h"
SymbolTable symbolTable;
void parseAssignments();
int main()
{
Expression* expression;
char paren, comma;
string program;
ifstream myfile("expressions.txt");
if (myfile.is_open())
{
while (getline(myfile, program))
{
cout << program << '\n'; //confirms that file was read properly
//code to parse expressions and print results
}
myfile.close();
}
else cout << "Unable to open file";
//original code that parses expression the user inputs
/*cout << "Enter expression: ";
cin >> paren;
expression = SubExpression::parse();
cin >> comma;
parseAssignments();
cout << "Value = " << expression->evaluate() << end1;*/
cin.get(); //added this so the window doesn't close automatically
return 0;
}
I have tried numerous approaches over the last couple days, and I think it comes down to my C++ ignorance of what exactly is happening on these two lines. My theory is that it parses everything to the right of the parenthesis in the first line, and everything to the right of the comma in the second line (ignoring the fact that the scopes would overlap).
cin >> paren;
cin >> comma;
Therefore, among other approaches, I tried including the following in the while loop. I am using MS Visual Studio, and when I click "> Local Windows Debugger" it will open up a console window with the first expression printed and a blinking line like it's waiting for me to input something. I close out of the window, and instead of a "> Continue" in place of "> Local Windows Debugger" it will just end the program. It is entirely possible that I'm just using Visual Studio incorrectly.
myfile >> paren;
expression = SubExpression::parse();
myfile >> comma;
parseAssignments();
cout << "Value = " << expression->evaluate();
And this:
std::istringstream iss(program);
iss >> paren;
expression = SubExpression::parse();
iss >> comma;
parseAssignments();
cout << "Value = " << expression->evaluate();
And of course this:
getline(myfile, program) >> paren;
expression = SubExpression::parse();
getline(myfile, program) >> comma;
parseAssignments();
cout << "Value = " << expression->evaluate();
Am I taking the completely wrong approach here, or worse should I be focusing on changing other functions as well? Thank you!!
EDIT: It appears that cin >> was being called in parse() and a few other .cpp files, which prevented my three attempts above from having much of an effect. Right now I'm looking into including the line as an argument in functions like below. I will report any errors if it doesn't work.
string parseName()
{
char alnum;
string name = "";
cin >> ws;
while (isalnum(cin.peek()))
{
cin >> alnum;
name += alnum;
}
return name;
}
EDIT 2: IT WORKS!!! Since cin >> was being used throughout the program, I had to create a new variable std::stringstream in(program); and then use that as arguments in functions as seen below:
void parseAssignments(stringstream& in);
int main()
{
Expression* expression;
char paren, comma;
string program;
ifstream myfile("expressions.txt");
if (myfile.is_open())
{
while (getline(myfile, program))
{
cout << program << '\n'; //confirms that file was read properly
std::stringstream in(program);
in >> paren;
expression = SubExpression::parse(in);
in >> comma;
parseAssignments(in);
cout << "Value = " << expression->evaluate() << '\n\;
}
myfile.close();
}
else cout << "Unable to open file";
cin.get(); //added this so the window doesn't close automatically
return 0;
}
Thanks for your help guys! I think I'm going to go back to something a bit simpler with C++ and build up from there...
Lets start with this
cin >> paren;
expression = SubExpression::parse();
cin >> comma;
This code reads a single character from cin (the terminal), then calls parse, then reads another single character. It never reads your expression. It also never does anything with the two single characters, it just throws them away.
This says to me that the code that reads the expression from the terminal is in SubExpression::parse() -- that code (which you don't show) apparently reads directly from cin.
This indicates immediately why all your attempts have failed -- you have your expression in a file or a string, but your parse routine is still stubbornly reading from cin. You need to change that routine to read from somewhere else.
Related
I was going through a code in my school textbook, wherein there is a line who's function is to clear the input buffer (mentioned as a comment in the code).
I couldn't quite understand its purpose. It is definitely required as its removal messes up the console input process.
Please explain what its function is, and what is happening when I remove it.
I have also tried using cin.ignore(); and it works just fine too. How is the function used here, is it an exact replacement of cin.ignore()?
P.S. In school we are using the older version of C++. Hence the ".h" extension, clrscr();, etc.
#include <iostream.h>
#include <fstream.h>
#include <conio.h>
void main(){
clrscr();
ofstream fout("student.txt", ios::out);
char name[30], ch;
float marks = 0.0;
for(int i = 0; i < 5; i++){
cout << "Student " << (i+1) << ":\tName: ";
cin.get(name,30);
cout << "\t\tMarks: ";
cin >> marks;
cin.get(ch); //for clearing input buffer (This thing!)
fout << name << '\n' << marks << '\n';
}
fout.close();
ifstream fin("student.txt", ios::in);
fin.seekg(0);
cout << "\n";
for(int i = 0; i < 5; i++){
fin.get(name,30);
fin.get(ch); //Again
fin >> marks;
fin.get(ch); //Same
cout << "Student Name: " << name;
cout << "\tMarks: " << marks << "\n";
}
fin.close();
getch();
}
cin >> marks;
cin.get(ch); //for clearing input buffer (This thing!)
This is a not-so-robust way of clearing the input buffer. If you type a number followed by Enter, the first line will consume the number and put the value in marks while the second line will read the newline character and discard it.
It is not robust since it does not account for spaces a user might have entered after the number. A more robust method would be to use istream::ignore.
cin >> marks;
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
I just want to start with pointing out that you are not using "the older version of C++". You are just using some c code (like clrscr()) and c styled programming, and indeed probably not cpp11 or cpp14. Also header files for C++ are often still just .h.
Now the answer to your question:
cin.get(ch);
The '\n' character gets read into ch. '\n' is one character, a newline character.
does indeed do the same thing as
cin.ignore();
It is used to clear the input buffer, which means that the empty end of line ('\n') is being deleted from the input buffer.
Why is this done you ask? This is a good example of why you would do that. Hope this helped you!
I'm trying to make a program that will get the user input of a new file name, create the file, and write to it. It works but it will only write the first word of the string to the file. How can i get it to write the full string? thanks.
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main()
{
for (;;)
{
char *myFile = " ";
string f = " ";
string w = " ";
cout <<"What is the name of the file you would like to write to? " <<endl;
cin >>f;
ofstream myStream(f,ios_base::ate|ios_base::out);
cout <<"What would you like to write to " <<f <<" ? ";
cin >>w;
myStream <<w;
if (myStream.bad())
{
myStream <<"A serious error has occured.";
myStream.close();
break;
}
}
}
According to this post, you should consult this reference to use a method like getline().
Also, when you are writing out I recommend that you flush the output (cout.flush()) before ending the program, especially in this case, since I presume you are ending the program with a ctrl-C break.
In formulating a suggestion, I will read data into char*, and convert them to "string" in case you will use them elsewhere in your program.
I tested this code in MS Visual C++ Express.
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main()
{
for (;;)
{
char *myFile = new char[200]; // modified this line
//added this line
char *myInput = new char[200];
string f = " ";
string w = " ";
cout << "What is the name of the file you would like to write to? " << endl;
cin.getline(myFile, 200);//modified this line
f = (myFile);//added this line
cin.clear(); //added this line
ofstream myStream(f, ios_base::ate | ios_base::out);
cout << "What would you like to write to " << f << " ? ";
cin.getline(myInput, 200);//edited this line
w = string(myInput);//added this line
myStream << w;
myStream.flush();//added this line
if (myStream.bad())
{
myStream << "A serious error has occured.";
myStream.close();
break;
}
delete myFile;
delete myInput;
}
}
You have to use std::getline() to read the entire line:
std::getline(std::cin >> std::ws, w);
The >> std::ws part gets rid of leading whitespace which is needed because the newline left in the stream from the previous extraction will prevent std::getline() from fully consuming the input.
When inserting data into the stream you need to make sure it gets flushed (because, as the other answer said, you're probably using Ctrl+C to terminate the program and you may not see output during the program run). You can use the std::flush manipulator to flush the output:
myStream << w << std::flush;
cin<<w; cin would stop consuming input character when it encounter whitespace tab and other unseeable characters.
you should probably use std::getline() instead.
take a look at this page for ref.
http://en.cppreference.com/w/cpp/string/basic_string/getline
Or you can use manipulator to not skip whitespace.
Code:
#include<iostream.h>
#include<fstream.h>
#include<string.h>
int n = 0, flag = 0,i;
struct bac
{
char name[10];
char amt[5];
} s;
void main()
{
ofstream f("C:\\TC\\1.dat");
for(i=0;i<10;i++)
{
cout << "\nenter the details ";
cin >> s.name >> s.amt;
f.write((char *)&s, sizeof(bac));
}
}
sometimes the code works fine
but at the other times , when i look at the output file,it is empty , the problem has come up many times , and i ant to know whether there is a precaution regarding loops with file handling
for eg. in other program
.....
while(ch!=4)
{
cout << "\nBANK MANAGEMENT SYSTEM \n";
cout << "enter choice ";
cout << "\n1.add\n2.search\n3.delete and overwrite ";
cin >> ch;
if (ch == 1)
{
cout << "\nenter the details ";
cin >> s.name >> s.amt;
f.write((char *)&s, sizeof(bac));
}
.....
the file is empty
I guess you may have used a very old compiler older than gcc 4.5.3.
I tried your code and it has no problem.
#include <iostream> //use header file without using deprecated iostream.h
#include <fstream> //same reason as above
#include <string>
using namespace std;
int n = 0, flag = 0,i;
struct bac
{
char name[10];
char amt[5];
} s;
int main() //usually main returns int. void was kind of old now
{
ofstream f("test.txt");
for(i=0;i<10;i++)
{
cout << "\nenter the details ";
cin >> s.name >> s.amt;
f.write((char *)&s, sizeof(bac));
}
f.flush();
f.close();
return 0;
}
I compiled the code in gcc 4.5.3 and ran it. the file has all the stuff I entered.
However, it will be better to use the << operator when you use file i/o stream to write to file.
You can find more information about and from top of this link:
http://members.gamedev.net/sicrane/articles/iostream.html
Another point, wen you have done writing to a file, remember to flush and close the file handle, otherwise, sometimes it will cause some annoying problems.
The code doesn't seem very C++-like to me...
To answer the last question, there aren't any gotchas about fstreams in loops specifically, no.
I suggest first trying to do f.write with the members name and amt themselves—compilers might add padding between name and amt, creating unwanted garbage output.
Are you sure you have write permission to the filepath all the time? Try opening a local file, as in the path being simply "1.dat".
Also try opening the file as f("/* file name */", ofstream::out | ofstream::app). "out" sets it as an output stream, and "app" makes it add to the end of the file. www.cplusplus.com/ofstream details more flags.
since you are using c++, I suggest you use a formal way to use ofstream, in your code, it should be f << s.name << s.amt.
remember, you are using c++, so keep using i/o stream.
This question already has answers here:
Need help with getline() [duplicate]
(7 answers)
Closed 7 years ago.
This is probably a very simple problem but forgive me as I am new.
Here is my code:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main ()
{
string name;
int i;
string mystr;
float price = 0;
cout << "Hello World!" << endl;
cout << "What is your name? ";
cin >> name;
cout << "Hello " << name << endl;
cout << "How old are you? ";
cin >> i;
cout << "Wow " << i << endl;
cout << "How much is that jacket? ";
getline (cin,mystr);
stringstream(mystr) >> price;
cout << price << endl;
system("pause");
return 0;
}
The problem is that when asked how much is that jacket? getline does not ask the user for input and just inputs the initial value of "0". Why is this?
You have to be careful when mixing operator>> with getline. The problem is, when you use operator>>, the user enters their data, then presses the enter key, which puts a newline character into the input buffer. Since operator>> is whitespace delimited, the newline character is not put into the variable, and it stays in the input buffer. Then, when you call getline, a newline character is the only thing it's looking for. Since that's the first thing in the buffer, it finds what it's looking for right away, and never needs to prompt the user.
Fix:
If you're going to call getline after you use operator>>, call ignore in between, or do something else to get rid of that newline character, perhaps a dummy call to getline.
Another option, and this is along the lines of what Martin was talking about, is to not use operator>> at all, and only use getline, then convert your strings to whatever datatype you need. This has a side effect of making your code more safe and robust. I would first write a function like this:
int getInt(std::istream & is)
{
std::string input;
std::getline(is,input);
// C++11 version
return stoi(input); // throws on failure
// C++98 version
/*
std::istringstream iss(input);
int i;
if (!(iss >> i)) {
// handle error somehow
}
return i;
*/
}
You can create a similar function for floats, doubles and other things. Then when you need in int, instead of this:
cin >> i;
You do this:
i = getInt(cin);
Its because you have a '\n' left lying on the input stream from a previous call.
cin >> i; // This reads the number but the '\n' you hit after the number
// is still on the input.
The easiest way to do interactive user input is to make sure each line is processed independently (as the user will hit enter after each prompt).
As a result always read a line, then process the line (until you get familiar with the streams).
std::string line;
std::getline(std::cin, line);
std::stringstream linestream(line);
// Now processes linestream.
std::string garbage;
lienstream >> i >> garbage; // You may want to check for garbage after the number.
if (!garbage.empty())
{
std::cout << "Error\n";
}
Ignore some characters until line feed is reached.
cin.ignore(256, '\n')
getline (cin,mystr);
I'm trying to read from files created outside of the program, but am having some trouble. The program has the user create a file. Then it reads words from two .txt files created outside of the program, and then writes the words to the created file.
#include "std_lib_facilities.h"
int main()
{
string word;
cout << "Create file.\n";
char name[20];
cin >> name;
ofstream ost(name, ios::out);
cout << "Open first file.\n";
char filename[20];
cin >> filename;
ifstream ist(filename);
while(ist >> word) ost << word << " ";
ist.close();
cout << "Open second file.\n";
cin >> filename;
ifstream isttwo(filename);
while(isttwo >> word) ost << word << " ";
isttwo.close();
ost.close();
keep_window_open();
}
However, when I open the created file in notepad, it comes out blank. Is this because reading into a string is impossible because the files being read were created separately? I'm not really sure. Any help is appreciated.
The code is correct. Just make sure when you write the name of the first file and the second one you write their extensions as well.
For example :
first.txt
second.txt
A potential problem is your use of cin instead of getline.
Imagine this input:
Open second file.
>>file one.txt
This obviously won't open the file you want, since cin stops reading once you hit a space.
Try explicitly opening name, such as:
ost.open(name);
I also don't like your implicit use of the >> operator in the while loops. Try this:
while (!ist.eof())
{
getline(ist, word);
ost << word;
}
Maybe you should null terminate the filename[20] string . Like filename[20]={0}; before getting it from the standard input ; Then try printing out the filename on the console to check if the file name is OK .
Trikker,
The code compiled with insignificant changes and I came across the same problem you are having, though actually it works. Here's what happened with me. It has to do with how you are running the program. The exe file is saved in ../ProjectDir/Debug directory. If you save the two input files in Debug directory, you should run the program from the command prompt, NOT from the IDE (assuming Visual Studio 2008). However, if you run it from the IDE, save the two input files in ../ProjectDir/ProjectDir. Once I did that everything was ok with me.
Just to be sure about this directory thing, I have printed the directory from which the program is being run from the IDE. Though, the prompt shows that it is running the exe from the Debug directory, the working directory was actually ProjectDir/ProjectDir.
Anyway, please, give the following code a shot and do let us know?
//#include "std_lib_facilities.h"
#include <fstream>
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
system("cd");
//string word;
char word[80];
cout << "Create file.\n";
char name[20];
cin >> name;
ofstream ost(name, ios::out);
cout << "Open first file.\n";
char filename[20];
cin >> filename;
ifstream ist(filename);
while(ist >> word) ost << word << " ";
ist.close();
cout << "Open second file.\n";
cin >> filename;
cout << filename;
ifstream isttwo(filename);
while(isttwo >> word) ost << word << " ";
isttwo.close();
ost.close();
//keep_window_open();
}
This directory issue also probably explains why you didn't get anything, when tried to print out if it was reading anything at all. You probably should have checked if the input file creation was successful. To do that, just add ...
if ( ist.fail() ){
cout << "file open failed\n";
}
after
ifstream ist(filename)
What happens if you use absolute paths? e.g. on Windows try c:\file.txt and so on.