Reputation: 35
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSIZE 10
int main(int argc, char *argv[]){
char input[MAXSIZE], c, *input_ptr;
input_tag: printf("Enter the initial data string : ");
input_ptr = fgets(input, MAXSIZE, stdin); //get input
if(input_ptr != NULL && strcspn(input,"\n") == MAXSIZE-1){ //check if input fits in buffer
printf("Buffer overflow. Reduce size of input.\n");
while((c = getchar()) != '\n') //loop through rest of STDIN to discard extra characters until newline
goto input_tag; //Request for user input again
}
input[strcspn(input,"\n")] = '\0'; //Remove newline character
printf("Input is %s\n", input);
return 0;
}
Is it possible to keep requesting the user for new input without using the goto statement? Is there a better way to do what I have done? Because, I keep reading that using goto statement should be avoided if possible.
Upvotes: 0
Views: 144
Reputation: 154146
A sample alternative.
It is not that great considering the restrictive assert()
and trouble when reading null characters.
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
/*
* Read a line - primarily via fgets() using prompts.
*
* Save up to size-1 characters and form a string. Do not save a '\n'.
*
* Return NULL on nothing more read or input error.
* Re-read if input is too long.
* Otherwise return buffer.
*/
char* read_a_line(size_t size, char *buffer, const char *prompt,
const char *reprompt) {
// Test parameters.
// Some reasonable functionality is still possible with parameters that do not meet the assert.
// Yet for simplicity of this demo code, they are enforced via an assert().
assert(size > 1 && size <= INT_MAX && buffer);
size_t length;
for (;;) {
if (prompt) {
fputs(prompt, stdout);
}
// Get the line.
if (fgets(buffer, (int) size, stdin) == NULL) {
return NULL;
}
length = strlen(buffer);
// Was less then the entire buffer used?
// Note that reading a null character fools this code.
// Hence a weakness of using `fgets()`.
if (length + 1 < size) {
break;
}
// Was the last character read a '\n'?
if (buffer[length] == '\n') {
break;
}
// If the _next_ character read is the end of the line,
// then that is OK.
int ch = fgetc(stdin);
if (ch == '\n') {
break;
}
if (ch == EOF) {
return feof(stdin) ? buffer : NULL /* Input error */;
}
// Line is too long. Now read rest of it.
// Here we could use `fgets()` again,
// yet the various cases complicate code, so go for simplicity.
do {
ch = fgetc(stdin);
if (ch == EOF) {
return NULL;
}
} while (ch != '\n');
prompt = reprompt;
}
// Lop off a potential '\n'.
if (length > 0 && buffer[length - 1] == '\n') {
buffer[--length] = '\0';
}
return buffer;
}
Upvotes: 1
Reputation: 2528
A do/while loop can be used to avoid goto
.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSIZE 10
int main ( void){
char input[MAXSIZE] = "";
int c = 0;
size_t count = 0;
do {
printf("Enter the initial data string : ");
if ( NULL == fgets(input, MAXSIZE, stdin)) {
fprintf ( stderr, "problem fgets returned NULL\n");
return 1;
}
count = strcspn ( input, "\n"); // count to newline or terminating zero
// printf ( "count %zu\n", count);
if ( input[count] != '\n') { // did not count to newline
while ( ( c = getchar ( )) != '\n') {
if ( EOF == c) {
if ( 0 != count) {
break;
}
fprintf ( stderr, "found EOF\n");
return 1;
}
}
if ( EOF != c) {
printf ( "\nBuffer overflow. Reduce size of input.\n");
}
}
} while ( input[count] != '\n' && EOF != c);
input[count] = '\0'; //Remove newline character
printf ( "Input is %s\n", input);
return 0;
}
Upvotes: 2
Reputation: 154146
Is there a better way to do what I have done?
Yes.
Code has at least these problems:
No loop
while((c = getchar()) != '\n') goto input_tag;
does not loop.
Maybe OP wanted while((c = getchar()) != '\n'); goto input_tag;
.
IAC, a goto
is not needed. Another while()
loop is a better alternative.
Potential UB
input[strcspn(input,"\n")] = '\0';
is attempted when the prior fgets(input, ...)
returns NULL
. input[]
might not contain string.
Likewise for printf("Input is %s\n", input);
.
Infinite loop
char c; ... while((c = getchar()) != '\n')
is an infinite loop when getchar()
returns EOF
due to an end-of-file.
c
should be an int
.
while((c = getchar()) != '\n' && C != EOF)
is more common and not so error prone.
Wrong check in a corner case
strcspn(input,"\n") == MAXSIZE-1
is not certainly wrong when the next character read is an end-of-file indication.
I do not see a great alternative solution using fgets()
to read a line.
Things to consider (some are pedantic):
Form a helper function.
How to indicate that the line is too long.
How to cope when null characters are read? How to inform caller of # of characters read?
Proper handling of an input error vs. end-of-file.
Distinguishing reading some text and then encountering end-of-fife vs. a line.
Reading into a buffer longer than INT_MAX
- uber pedantic.
IMHO, C lacks a great read_a_line()
function.
I'd want something like:
int read_a_line(FILE *stream, size_t dsize, char dest[dsize], size_t *length_read)
returning 1 on unqualified success, EOF
on nothing read/input error and 0 otherwise.
Good luck.
Upvotes: 5