Well, for starters, you really want to decide whether you are coding in C or C++. You #include <iostream>
, but make no use of any iostream functions (presumably you thought about using std::cout
or the like). You also specify using namespace std;
, but that is not valid in C. Rule: only include necessary headers for the functions used in your code.
We will presume you are wanting to write code in C given your choice of input and output functions. If not, drop a comment and I'm happy to help with C++ as well. Working though your code:
max 5
-- Don't SKIMP on buffer size. While you would use std::string
if writing in C++, when declaring an array to hold input in C, don't skimp. While fgets()
will only attempt to write size
number of characters (minus 1
to provide room for the nul-terminating character) your purpose in using fgets()
, among others, is to read a complete line of input at a time without leaving extraneous characters in the input stream. You do that by providing a sufficiently sized buffer.
- When taking input, you generally loop-continually and then break your read-loop if the input fails stream error or manual
EOF
(generated by the user pressing Ctrl + d (or Ctrl + z on Windows)), or successful input is received, or one of your exit conditions is satisfied (e.g. the user pressing Enter alone for the string indicating they are done with input)
- In C, to trim the
'\n'
from the end of the string read by fgets()
(e.g. fgets (string, MAXC, stdin)
), then you simply call string[strcspn (string, "\n")] = 0;
effectively overwriting the '\n'
with the nul-terminating character '\0'
(which is just ASCII 0
)
- Declare your variables in the scope where they are needed, and
- There is absolutely nothing wrong with
goto
used properly. It is the only way to break nested loops in a single expression, etc..
With that as a backdrop, you can simplify and clean up your code similar to the following. Many of the choices are up to you, but this is one general way to approach
- reading input,
- checking if the input is valid,
- checking if the user generated an
EOF
,
- checking if the user is done with input,
- checking if the input matches a string you want, and finally
- looping again if the input isn't what is needed.
(a rough interpretation of what it looked like you were wanting to do)
The code (with additional comments) can be:
// #include <iostream> /* only include needed headers for functions used */
#include <stdio.h> /* or <cstdio> for modern C++ */
#include <string.h> /* or <cstring> for modern C++ */
// using namespace std; /* using nothing from std:: namespace */
#define MAXC 1024 /* don't SKIMP on buffer size, UPPERCASE defines */
#define ANS "guess"
int main()
{
char string[MAXC] = {0}; /* declare string */
/* read-loop continually, break on condition of your choice */
while (1) {
fputs ("\nEnter string: ", stdout); /* no conversion, fputs is fine */
/* validate EVERY input based on return of read function */
if (!fgets (string, MAXC, stdin)) {
puts ("(user generated manual EOF)");
return 0;
}
/* trim '\n' from string */
string[strcspn (string, "\n")] = 0;
/* exit if empty-string (user just pressed [Enter], or any condition) */
if (*string == '\0') {
puts ("(all done)");
break;
}
/* check if correct answer ANS given */
if (strcmp (string, ANS) == 0) {
puts ("(correct!)");
break;
}
puts ("(answer didn't match)"); /* otherwise, loop again */
}
puts ("(That's All Folks!)");
}
Example Use/Output
$ ./bin/read-loop
Enter string: my dog has fleas
(answer didn't match)
Enter string: why?
(answer didn't match)
Enter string: what if I guess?
(answer didn't match)
Enter string: guess
(correct!)
(That's All Folks!)
Or canceling with a Ctrl + d:
$ ./bin/read-loop
Enter string: (user generated manual EOF)
Or using the exit-condition of the user just pressing Enter alone at the prompt indicating they are done guessing:
$ ./bin/read-loop
Enter string: next we will just press Enter when prompted, to quit input
(answer didn't match)
Enter string:
(all done)
(That's All Folks!)
You can add (or remove) as many of the exit conditions for the read-loop as you like. The key take-aways are:
- loop continually until the user provides the required input,
- always condition exiting your read-loop on the return of your read-function,
- validate EVERY user-input,
- provide the needed exit conditions to control exit from your read-loop, and
- handle any errors that occur as needed.
Let me know if this is what you needed. If not, I'm happy to help further.
killNL()
you are looking forEOF
but that isn't a character that should appear in a string: it is a flag which is returned by file reading functions. \$\endgroup\$killNL()
function is supposed to remove the trailing newline retained byfgets()
then please see this easy one-line solution: Removing trailing newline character from fgets() input \$\endgroup\$while (*(str+i) != EOF){
is problematic on two counts: (1) it is better to usestr[i]
than*(str+i)
, and (2) comparing characters with EOF is very dubious (you should be looking for end of string, EOS, which is a null byte,'\0'
, not EOF (which is a negative integer value, usually-1
). If the basechar
type is signed, you might stop on a valid character (in some code sets, that would be ÿ — small Latin letter Y with diaeresis in Unicode speak). If the basechar
type is unsigned, you'll never match. In both cases, as written, the code is more likely to crash than behave. \$\endgroup\$while (c = (getchar()) != '\n' && c != EOF)
is definitely wrong. The second(
is misplaced. It should bewhile ((c = getchar()) != '\n' && c != EOF)
(do you see the difference?). \$\endgroup\$killNL()
function in a book as you say, please destroy that book so that it can't harm any more people. It makes absolutely no sense to compare or assignchar
withEOF
, which is anint
value distinct from any valid char. (Not to mention its unidiomatic practice of spelling out the indexing arithmetic in longhand, rather than using the more readily understood[]
operator) \$\endgroup\$