Simple File I/O in C++ - Never Exits This Loop? - c++

I'm a programming student in my second OOP class, my first class was taught in C# and this class is taught in C++. To get our feet wet in C++, our professor has asked us to write a rather large program with File I/O. The problem is, I have a small part of my program that is not working, at least, not for me. The project requires that we code a loop to test if the file could be opened successfully, and the loop I have written doesn't seem to be working.
I don't get any compiler errors, but when I enter in the path to the file, either relative or absolute, it says it's invalid. I have a feeling it has something to do with my conditions in my do-while loop, but I can't pinpoint it.
I don't mean to bring my homework to SO, but I've been scratching my head for two+ hours, and I can't seem to figure this out.
Would you mind helping me fix my loop? And maybe explain what it is that I'm doing wrong? I want to learn.
Thanks!
Code:
Rainfall rData;
ifstream actualReader;
ifstream averageReader;
string aRDataLoc;
char response = 'a';
const int KILL_VALUE = 1;
double actualRainfallD;
double actualRainfallPassedArray[ARRAY_CAPACITY];
double averageRainfallPassedArray[ARRAY_CAPACITY];
int i = 0;
do
{
actualReader.clear();
cout << "\nPlease enter in the path to the file containing the actual rainfall data." << endl;
cout << "Path to file: ";
cin >> aRDataLoc;
actualReader.open(aRDataLoc.c_str());
if (!actualReader.is_open())
{
cout << "Invalid file path! Would you like to enter in a new file path?(y/n): ";
cin >> response;
if (response == 'n') { exit(KILL_VALUE); }
}
}while (!actualReader.is_open() && response == 'y');

I don't know what input you are giving to cin, but be aware that cin will stop at the first whitespace character it encounters. For example, if you give as input the following:
C:\Program Files\directory
then aRDataLog would have the value C:\Program .
In order to read the whole line, you could use getline.
Check also this post.

While I'm not exactly sure what the issue is here, it might be a good idea to print what you're getting from std::cin to ensure you're getting what you're expecting.

you need to put actualReader.close call in else, as the file has open handles and it is not available for open again

Related

Proper use of EOF (can it be used multiple times in a program?)

In my intro to programming course (c++), the instructor told us to:
"Write a program that inputs an unspecified number of integer ("int")
values and then outputs the minimum value and the maximum value that
were entered. At least one value will always be input. (read all input
until EOF). For extra credit make your program also work when the user
doesn't enter any ints at all (just the EOF.)"
I wanted to get fancy, so, in my solution, when just EOF is entered, the program responds with "Oops! You didn't enter anything. Please try again, this time entering at least one integer: " and prompts for input again.
My instructor is saying that this answer is wrong because
After the EOF, there should be no more input to a program (neither
expected by the user nor the program) — using the EOF to switch from
“one mode” of input to another mode of input isn’t supporting the
standards.
Every definition of EOF I've found on the internet doesn't seem to support my professor's definition. EOF, from what I can tell, is simply defined as the end of the current file. It seems perfectly valid to accept input from a user until EOF, do something with that input, and then ask for additional input until EOF again.
Because this is an online course, I was able to review everything we learned relating to EOF and we were only told that EOF meant "End of File" and could be 'used to signal an end to user input' (important, because, even if my professor was wrong, one could argue that I should have adopted his standards if he had specifically told us to. But he didn't tell us to).
What is the proper way to use EOF with user input? Is my professor's statement that "After the EOF, there should be no more input to a program" the standard
and expected way to use EOF? If a program accepts a variable amount of input, does something with it, and then accepts more variable input, is it not acceptable to use EOF with those inputs (aka don't use while(cin >> user_input) in that scenerio)? If so, is there a standard for what should be used to signal end of input in a scenario where you're accepting variable input multiple times?
My exact solution to the assignment is below. My solution to the main assignment "Write a program that inputs an unspecified number of integer ("int") values and then outputs the minimum value and the maximum value that were entered" was considered correct, by the second part of the assignment "make your program also work when the user doesn't enter any ints at all (just the EOF.)" was deemed incorrect ("make the program also work" is the only prompt we were given).
Thanks so much for any feedback!! Obviously, I'm skeptical of my professors feedback / decision, but, in general, I'm just trying to get a sense of C++ community standards.
#include <iostream>
#include <iomanip>
#include <string>
#include <stdlib.h>
using namespace std;
int main(){
string user_input;
int int_input, min_user_input, max_user_input;
bool do_it = true;
cout << "Hi John," << endl;
cout << "Please enter a few integers (signal EOF when finished): ";
while(do_it) {
cin.clear();
cin >> user_input;
if (user_input.empty()) {
cout << endl;
cout << "Oops! You didn't enter anything. Please try again, this time entering at least one integer: ";
}
else {
try {
int_input = atoi( user_input.c_str() );
min_user_input = int_input;
max_user_input = int_input;
while(cin >> int_input) {
if (min_user_input > int_input) {
min_user_input = int_input;
}
if (max_user_input < int_input) {
max_user_input = int_input;
}
}
cout << endl;
cout << "The max user input was: " << max_user_input << endl;
cout << "The min user input was: " << min_user_input << endl;
do_it = false;
}
catch (std::invalid_argument) {
cout << endl;
cout << "Oops! You didn't enter an integer. Please try again, this time only entering integers: ";
do_it = true;
}
}
}
return 0;
}
Note: additional feedback I got on this submission was: to not use c libraries (apparently stdlib.h is one) and that, on some computers (though, apparently, not mine), #include <stdexcept> will be needed to compile.
Answer
Short answer: my instructor is correct. When used with cin, no additional user input should follow an EOF signal. Apparently, in some cases the program won't let you enter more information, but, as #hvd points out
Although your system may let you continue reading from the same file
in the specific case that it is coming from a TTY, due to EOF being
faked there, you shouldn't generally rely on that.
Aka, because I'm using a terminal to enter user input, the program happens to work. In general, it won't work though.
As #RSahu answers, EOF just shouldn't be used to signal the end of variable length cin multiple times in a program. Importantly
There is no standard means, or commonly practiced coding standard, of
indicating when user input has ended for the time being. You'll have
to come up with your own mechanism. For example, if the user enters
"end", you can use it to deduce that the user has ended input for the
time being. However, you have to indicate to the user that that's what
they need to enter. Of course, you have to write code to deal with
such input.
Because this assignment required the use of EOF, what I was attempting to accomplish was, unintentionally, prohibited (aka receive input, check it, possibly receive more input).
Proper use of EOF (can it be used multiple times in a program?)
There is no single EOF. There is EOF associated with every input stream.
If you are reading from a file, you can reset the state of the std::ifstream when it reaches EOF to allow you to read the contents of the file again.
However, if you are reading data from std::cin, once EOF is reached, you can't read from std::cin any more.
In the context of your program, your professor is right. They are most likely talking about reading from std::cin.
EOF, from what I can tell, is simply defined as the end of the current file.
It is. Note that in particular, what it doesn't mean is the automatic start of a new file.
Although your system may let you continue reading from the same file in the specific case that it is coming from a TTY, due to EOF being faked there, you shouldn't generally rely on that. Try program </dev/null and see happens when you try to automate your program.

While loop acting weird in regards to input

Been working on a simple school project but I've run into a small problem in regards to validation of user input via the use of a while loop. The following code is used to do so:
cout << "How much woud you like to bet?[100, 300, 500]" << endl;
cin >> int_input;
if (cin.fail()){
int_input = 0;
}
//Validate check
while(int_input!=100 && int_input!=300 && int_input!=500){
cout << "Please enter a valid bet [100, 300, 500]" << endl;
cin >> int_input;
if (cin.fail()){
int_input = 0;
}
}
The issue arises when a non-integer is entered and the loop ends up spitting out the cout msg over and over again, skipping the request for new input. I assume that a non-integer always returns true for the while loop, but the program should still ask for user input, right?
The cin.fail is a recent addition and might not be necessary as it doesn't solve the problem, although I leave the code as it as it is.
If you have an error on the stream, you set your value to 0. So the loop is run again... and the error's still on the stream. Repeat.
You need to clear the error flag.
cin.clear();
Then extract enough "bad" data to get to the good part, or you'll just be right back where you started.

Stop new line for C++ cin

In C++, iostream automatically puts in a new line after cin. Is there a way to get rid of this?
I want use iomanip to format information into a table, like so:
cin cout
0.078125 3DA00000
-8.75 C10C0000
23.5 41BC0000
(random numbers)
example code:
#include <iostream>
using namespace std;
int main()
{
int num;
cin >> num; //now a new line.
cout << num << endl;
return 0;
}
You presumably pressed the return key to send your input from the command line to your program's standard input. That's where the newline is coming from. You can't read your number from cin before this newline appears in the console, because the newline is what causes the console to hand your input over to the program in the first place. You could, as a user, configure your console (or whatever is running your program) to act differently, but there's no way for the program itself to force such behavior.
If you really want to have your input and your output on the same line, you need to find a way to "write to the previous line". How that works depends on your console (see also How to rollback lines from cout?). There is no standard way to do this because cin and cout are in no way obligated to be attached to a console or anything resembling one, so it is not clear that "writing to the previous line" even means anything.
'endl' makes a new line just don't use it.
cout << num;

Visual C++ using Console: Char/String compatibility issues with while loop

cout << "Would you like to make another transaction? (y/n)" << endl;
cin >> repeat_transaction;
static_cast<char>(repeat_transaction);
while (repeat_transaction != 'y' && repeat_transaction != 'n')
{
cout << "Invalid selection: Please enter y or n";
cin >> repeat_transaction;
static_cast<char>(repeat_transaction);
}
During the Invalid selection loop, I once accidentally pressed "mn". I noticed the console read out Invalid selection..., So, it did in fact finish and re-enter the while loop. However, after this the console terminated the program. If you enter a single character 'a' or 'y' or 'n' it acts just as it should. Ending or not ending. This was before I attempted to use static_cast to force the truncation of the user input.
Since you managed to get this program to compile I can only assume that repeat_transaction was specified as a char and not a std::string.
When you use cin to get a character it only gets one character but it doesn't flush the buffer. I believe you understand this issue since you wrote This was before I attempted to use static_cast to force the truncation of the user input. . You can attempt to use cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); instead of static_cast<char>(repeat_transaction); after each call to cin >> repeat_transaction; . There are downsides to this. If you enter 'mn' it will work as expected. It reads the m which is not y or n and then flushes the extra characters until it finds end of line \n. If you do nm, n will match and the m will be thrown away. So in that case it will accept nm as valid and exit the loop.
There are other ways that may be easier and give you the effect closer to what you are looking for. Instead of reading a character at a time you can read an entire line into a string using getline (See the C++ documentation for more information). You can then check if the length of the string is not equal to 1 character. If it's not length 1 then it is invalid input. If it is 1 then you want to check for y and n. Although basic (and not overly complex) this code would do a reasonable job:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string repeat_transaction;
cout << "Would you like to make another transaction? (y/n)" << endl;
getline(cin, repeat_transaction);
while (repeat_transaction.length() != 1 || (repeat_transaction != "y" && repeat_transaction != "n"))
{
cout << "Invalid selection: Please enter y or n";
getline(cin, repeat_transaction);
}
return 0;
}
I said reasonable job since one deficiency you might see is that you want to trim white spaces from the beginning and end. If someone enters n or y with a space or tab in front it will be seen as invalid (whitespace at the end would be similar). This may not be an issue for you, but I thought I would mention it.
On a final note, you may have noticed I used using namespace std;. I did so to match what was in the original question. However, this is normally considered bad practice and should be avoided. These StackOverflow answers try to explain the issues. It is better to not do it and prepend all standard library references with std::. For example string would be std::string, cin would be std::cin etc.

Check if "cin" is a string

I have a simple little script I am coding, and I am trying to not allow people to enter a string, or if they do make it revert to the beginning of the function again. Here's the input code I have:
int main()
{
cout << "Input your first number" << endl;
cin >> a;
cout << "Input your second number" << endl;
cin >> b;
}
The rest of the code beyond this part works just fine for what's going on, although if a string is entered here it obviously doesn't work.
Any help would be appreciated.
You may find this post useful,
How to check if input is numeric in C++
Basically you can check the input, whether it is numeric value or not. After checking whether the given input is numbers, then you can add a while loop in main to ask user to repeat if input is not a valid number.
Every input is a string. If you want to know if a entered string can convert to a number, you have to read in a string and try to convert it yourself (eg with strol).
An alternative would be to check if the reading from cin failed, but personally i don't like it because cin.fail() covers more error situations than just a failed type conversion.
There's a library function may help,you can check it after input:
int isdigit(char c);
Tips:
1.You should include such files :
# include <ctype.h>
2.If c in 0 ~ 9 ,return 1 ; else return 0.