Reputation: 91
I was wondering what the problem with my program is. I can't get the program to exit when I type quit. Here is what I have:
#include <stdio.h>
#include <string.h>
int main(void) {
char string[200];
printf("Enter a bunch of words: ");
do
{
scanf("%[^\n]c", string);
}while(strcmp(string,"quit")!=0);
return 0;
}
Upvotes: 1
Views: 2987
Reputation: 84642
Your two biggest problems are two of the most common problems that plague new C programmers with their use of scanf
:
- You are using an incorrect format string; and
- You fail to check the return of
scanf
.
Let's address first things first:
scanf("%[^\n]c", string);
Your format string "%[^\n]c"
uses the character-class format specifier "%[...]"
to read the text for string
. It is then followed by "c"
-- which will only match a literal 'c'
at the end of your input string. That cannot happen as written because "%[^\n]"
will read all characters that are not the '\n'
leaving only the '\n'
to be read -- which does NOT match 'c'
.
Further, the "%[...]"
specifier along with the "%c"
specifier do NOT consume leading whitespace (the '\n'
being whitespace). So having left the '\n'
unread in stdin
your next call to scanf
fails because "%[^\n]"
will not read the '\n'
and it doesn't match 'c'
leading to a matching failure, the '\n'
remains unread in stdin
and things quickly spiral out of control.
To solve all of the problems you need to remember (2.)
above and also use a field-width modifier to protect the array bounds of string
, and you should then read and Save the character following those extracted and put in string
to validate a complete line of input was read -- and if not, it is your responsibility to remove any excess characters that remain in stdin
before attempting your next read.
For starters, you can use a properly limited format string that includes a space
at the beginning that will cause scanf
to discard all leading whitespace, e.g.
" %199[^\n]%c"
Note above the final character will be saved, two conversions will take place, so you will need a character variable to handle result of the final conversion specifier, e.g.
do {
char c; /* final character read */
int retn; /* variable to save scanf return */
/* prompt */
fputs ("Enter a bunch of words ('quit' exits): ", stdout);
/* read saving scanf return */
retn = scanf (" %199[^\n]%c", string, &c);
(note: the prompt has been moved within the do {...} while (..);
loop)
Next you are responsible for checking the return of scanf
every time. You must handle three conditions
(return == EOF)
the user canceling input by generating a manual EOF
by pressing Ctrl+d (or on windows Ctrl+z);(return < expected No. of conversions)
, you must handle the matching or input failure and you must account for every character that may be left in your input buffer. (generally you will scan forward in the input buffer until a '\n'
or EOF
is found discarding any extraneous characters that remain, see the empty_stdin()
function in the example); and(return == expected No. of conversions)
indicating a successful read -- it is then up to you to check whether the input meets any additional criteria (e.g. positive integer, positive floating-point, etc..).Putting it altogether, you could handle you loop reading with scanf
and looking for "quit"
as the keyword prompting exit as follows:
do {
char c; /* final character read */
int retn; /* variable to save scanf return */
/* prompt */
fputs ("Enter a bunch of words ('quit' exits): ", stdout);
/* read saving scanf return */
retn = scanf (" %199[^\n]%c", string, &c);
if (retn == EOF) { /* check the return against EOF */
fputs ("(user canceled input)\n", stderr);
return 0;
}
else if (retn < 2) { /* checking both string and c read */
fputs ("input failure.\n", stderr);
empty_stdin();
}
else if (c != '\n') { /* check c is '\n', else string too long */
fprintf (stderr, "warning: input exceeds %d characters.\n",
MAXC - 1);
empty_stdin();
}
else /* good input, output string */
printf ("string: %s\n", string);
} while (strcmp (string,"quit") != 0);
Finally do NOT use magic-numbers in your code (200
is a magic-number). Instead, if you need a constant, #define
one (or more). The only place you must hard-code numbers are for example the scanf
field-width modifier -- which cannot use a variable, Macro or named constant. That is one exception to the rule. Likewise, do NOT hardcode filenames or paths. All functions take arguments, even main()
, pass the needed information to your program.
Putting it altogether, you could do something like:
#include <stdio.h>
#include <string.h>
#define MAXC 200 /* constant - maximum characters in string */
void empty_stdin (void)
{
int c = getchar();
while (c != EOF && c != '\n')
c = getchar();
}
int main (void) {
char string[MAXC]; /* use constants for array bounds */
do {
char c; /* final character read */
int retn; /* variable to save scanf return */
/* prompt */
fputs ("Enter a bunch of words ('quit' exits): ", stdout);
/* read saving scanf return */
retn = scanf (" %199[^\n]%c", string, &c);
if (retn == EOF) { /* check the return against EOF */
fputs ("(user canceled input)\n", stderr);
return 0;
}
else if (retn < 2) { /* checking both string and c read */
fputs ("input failure.\n", stderr);
empty_stdin();
}
else if (c != '\n') { /* check c is '\n', else string too long */
fprintf (stderr, "warning: input exceeds %d characters.\n",
MAXC - 1);
empty_stdin();
}
else /* good input, output string */
printf ("string: %s\n", string);
} while (strcmp (string,"quit") != 0);
return 0;
}
Example Use/Output
$ ./bin/scanf_string_quit
Enter a bunch of words ('quit' exits): Hello
string: Hello
Enter a bunch of words ('quit' exits): My dog has fleas and my cat has none.
string: My dog has fleas and my cat has none.
Enter a bunch of words ('quit' exits): quit
string: quit
Generating a manual EOF
with Ctrl+d (or Ctrl+z on windoze):
$ ./bin/scanf_string_quit
Enter a bunch of words ('quit' exits): Hello
string: Hello
Enter a bunch of words ('quit' exits): (user canceled input)
Resetting MAXC
to 20
and the field width modifier to scanf
to 19
you can check the handling of lines that are too long, e.g. the first input fits, the second is too long:
$ ./bin/scanf_string_quit
Enter a bunch of words ('quit' exits): my dog has fleas and my cat has none.
warning: input exceeds 19 characters.
Enter a bunch of words ('quit' exits): 1234567890123456789
string: 1234567890123456789
Enter a bunch of words ('quit' exits): 12345678901234567890
warning: input exceeds 19 characters.
Enter a bunch of words ('quit' exits): quit
string: quit
Look things over and let me know if you have further questions.
Upvotes: 4
Reputation: 70502
Given the sparse explanation you have provided, the most straightforward change may be to modify your scan string to swallow the \n
instead of a theoretical c
which is actually impossible:
scanf("%[^\n]\n", string);
To prevent buffer overflow, you should specify how much room your buffer has to store input:
scanf("%199[^\n]\n", string);
This is safe so long as you know the input will never surpass 199 characters. But, this is a weak assumption both in theory and practice. The characters past the 199 saved will get scanned as your next word on the next iteration, which would allow your program to quit unexpectedly if the input was 199 .
s followed by the word quit
. You need a more reliable way to scan to the rest of the line and discard it before reading the next line.
You might be tempted to use another %[...]
to capture the extra characters like this:
scanf("%199[^\n]%*[^\n]\n", string);
However, this will fail for the common case where the input is less or equal to 199 characters. This is because scanf
will fail if the conversion results in empty input. So, the \n
will remain jammed in the input.
If you are limited to using scanf
, then you will need to split the scanning into separate scanf
calls so that the error and non-error scan for the rest of the line can be treated as the same outcome, leading to a second scanf
to swallow the newline itself.
scanf("%199[^\n]%*[^\n]", string);
scanf("\n");
Upvotes: 0
Reputation: 704
Is something like this acceptable?
#include <stdio.h>
#include <string.h>
int main(void) {
char string[200] = {0};
printf("Enter a bunch of words: ");
do {
memset(string, 0, 200);
scanf("%s", string);
} while (strcmp(string, "quit") != 0);
return 0;
}
You haven't explained exactly what you intend to do with string, so it's difficult to give an answer. However, one thing to note is that you have to either do something to string
(I've just zeroed it out here) in order for strcmp
to recognize "quit", or scan substrings of string
, because if everything is always appended then your string will be "(...)quit" which strcmp won't recognize as "quit".
As a side note, always initialize your arrays or bad things could happen.
Upvotes: 1