User-Defined function for reading input not working - c

I've made a user-defined function for reading input and replacing newline character '\n' with '\0' so when I use printf statement for printing the string it won't add newline at the end.
char xgets(char *line, int size, FILE *stdn)
{
//READS THE LINE
fgets(line, size, stdn);
//REMOVES NEWLINE CHARACTER '\n' AND ADDS '\0'
line[strcspn(line, "\n")] = '\0';
return line;
}
When I call xgets inside main() function it works properly, but when it is called in other user-defined function it does not wait for user-input.
I'm using Visual Studio 2015 for debugging my code.
Here's my code:
#include<stdio.h>
#include<stdlib.h>
#include<process.h>
//USER-DEFINED FUNCTION
char xgets(char *line, int size, FILE *stdn);
void sortm_hgrade();
void sortm_rcharge();
void header(void);
void header(void)
{
printf("*-*-*-*-*HOTEL_INFO*-*-*-*-*");
printf("\n\n");
}
char xgets(char *line, int size, FILE *stdn)
{
//READS THE LINE
fgets(line, size, stdn);
//REMOVES NEWLINE CHARACTER '\n' AND ADDS '\0' END LINE CHARACTER
line[strcspn(line, "\n")] = '\0';
return line;
}
#define MAX 1000
//PROGRAMS STARTS HERE
int main(void)
{
//VARIABLE-DECLARATION
int i = 0, j = 0, n = 0;
char line[MAX] = { 0 };
char o = { 0 };
char h[10] = { 0 };
//FUCNTION CALL-OUT
header();
printf("Type anything : ");
xgets(h, sizeof(h), stdin);
printf("Enter one option from the following : \n\n");
printf("(a) To Print out Hotels of a given Grade in order of charges. \n");
printf("(b) To Print out Hotels with Room Charges less than a given Value. \n");
printf("Please type a proper option. \n");
while (n == 0){
scanf_s(" %c", &o);
switch (o){
case 'a':
sortm_hgrade();
n = 1;
break;
case 'b':
sortm_rcharge();
n = 1;
break;
default:
printf("Option INVALID \n");
printf("Please type a proper option \n");
n = 0;
break;
}
}
//TERMINAL-PAUSE
system("pause");
}
void sortm_hgrade()
{
//FOR SORTING BY GRADE
char g[10] = { 0 };
printf("Enter the Grade : ");
xgets(g, sizeof(g), stdin);
printf("\n");
}
void sortm_rcharge()
{
printf("----");
}

You should change
scanf(" %c", &o);
to
scanf("%c ", &o);
This force scanf to consume trailing chars, like '\n'
In your code '\n' of user input for scanf %c is not consumed and it is consumed by fgets in your xgets function that exit immediately with an empty buffer.
BTW that solution can wok only if a single char is input by user.
Best code would be
char c;
while (n == 0)
{
o = getchar();
while ((c = getchar()) != EOF && c != '\n') ;
EDIT
With the second solution code is waiting, and discarding, chars until a '\n' is triggered or end of file. In your specific case (using stdin as console) EOF is not mandatory. It will be mandatory in case of input is being read from a "real file".

You need to skip the \n character after you take in a character. you can command scanf for that. fgets reads that newline character up first and then hence it terminates. use this
scanf(" %c *[^\n]", &o);
This should do the trick

Related

Why my function read() doesn't work if i put some code before calling it?

When i call the function read() without putting a loop or something like that before it works perfectly but when i add some code it doesn't work, here is the code :
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
char read();
int main()
{
int level = -1;
char c;
while (level < 1 || level > 3)
{
printf("Select a level 1/2/3 : ");
scanf("%d", &level);
}
printf("Put a character : ");
c = read();
printf("Your character : %c", c);
return 0;
}
char read()
{
char letter;
letter = getchar();
letter = toupper(letter);
while (getchar() != '\n');
return letter;
}
The function getchar reads also white space characters as for example the new line character '\n' that can be placed in the input buffer after the call
scanf("%d", &level);
in the while loop.
Instead of getchar use a call of scanf within the function
char read()
{
char letter = '\0';
scanf( " %c", &letter );
letter = toupper(( unsigned char )letter);
return letter;
}
Pay attention to the space in the format string before the conversion specifier %c. It allows to skip white spaces in the input stream.
Or the function can look like
char read()
{
char letter = '\0';
scanf( " %c", &letter );
letter = toupper( ( unsigned char )letter );
int dummy;
while ( ( dummy = getchar() ) != EOF && dummy != '\n' );
return letter;
}

Function fgets skips user input?

When I use the function fgets, the program skips the user input, effecting the rest of the program. An example program with this effect is:
#include <stdio.h>
int main() {
char firstDigit[2];
char secondDigit[2];
printf("Enter your first digit: ");
fgets(firstDigit, 1, stdin);
printf("\nEnter your second digit: ");
fgets(secondDigit, 1, stdin);
printf("\n\nYour first digit is %s and your second digit is %s.\n", firstDigit, secondDigit);
}
I then thought that maybe the problem was that fgets might be writing the newline, so I changed the code to account for that:
#include <stdio.h>
int main() {
char firstDigit[3];
char secondDigit[3];
printf("Enter your first digit: ");
fgets(firstDigit, 2, stdin);
printf("\nEnter your second digit: ");
fgets(secondDigit, 2, stdin);
printf("\n\nYour first digit is %c and your second digit is %c.\n", firstDigit[0], secondDigit[0]);
}
This time, the first input works properly, but the second input is skipped.
What am I doing incorrectly?
char firstDigit[2] and char secondDigit[2] are not large enough to hold a digit, a newline character, and a null-terminator:
char firstDigit[3];
char secondDigit[3];
Then, the calls to fgets() need to specify the size of the buffer arrays:
fgets(firstDigit, sizeof firstDigit, stdin);
/* ... */
fgets(secondDigit, sizeof secondDigit, stdin);
When instead fgets(firstDigit, 2, stdin); is used, fgets() stores at most two characters, including the \0 character, in firstDigit[]. This means that the \n character is still in the input stream, and this interferes with the second call to fgets().
In answer to OP's comment, How would you remove the unread characters from the input stream?, a good start would be to use more generous allocations for firstDigit[] and secondDigit[]. For example, char firstDigit[100], or even char firstDigit[1000] will be large enough that any expected input will be taken in by fgets(), leaving no characters behind in the input stream. To be more certain that the input stream is empty, a portable solution is to use the idiomatic loop:
int c;
while ((c = getchar()) != '\n' && c != EOF) {
continue;
}
Note here that it is necessary to check for EOF, since getchar() may return this value if the user signals end-of-file from the keyboard, or if stdin has been redirected, or in the unlikely event of an input error. But also note that this loop should only be used if there is at least a \n character still in the input stream. Before attempting to clear the input stream with this method, the input buffer should be checked for a newline; if it is present in the buffer, the input stream is empty and the loop should not be executed. In the code below, strchr() is used to check for the newline character. This function returns a null pointer if the sought-for character is not found in the input string.
#include <stdio.h>
#include <string.h> // for strchr()
int main(void)
{
char firstDigit[3]; // more generous allocations would also be good
char secondDigit[3]; // e.g., char firstDigit[1000];
printf("Enter your first digit: ");
fgets(firstDigit, sizeof firstDigit, stdin);
/* Clear input stream if not empty */
if (strchr(firstDigit, '\n') == NULL) {
int c;
while ((c = getchar()) != '\n' && c != EOF) {
continue;
}
}
putchar('\n');
printf("Enter your second digit: ");
fgets(secondDigit, sizeof secondDigit, stdin);
/* Clear input stream if not empty */
if (strchr(secondDigit, '\n') == NULL) {
int c;
while ((c = getchar()) != '\n' && c != EOF) {
continue;
}
}
puts("\n");
printf("Your first digit is %c and your second digit is %c.\n",
firstDigit[0], secondDigit[0]);
return 0;
}
It may be even better to use a single buffer[] to store lines of input, and then to store individual characters in chars. You could also write a function to clear the input stream, instead of rewriting the same loop each time it is needed:
#include <stdio.h>
#include <string.h> // for strchr()
void clear_stdin(void);
int main(void)
{
char buffer[1000];
char firstDigit;
char secondDigit;
printf("Enter your first digit: ");
fgets(buffer, sizeof buffer, stdin);
firstDigit = buffer[0];
/* Clear input stream if not empty */
if (strchr(buffer, '\n') == NULL) {
clear_stdin();
}
putchar('\n');
printf("Enter your second digit: ");
fgets(buffer, sizeof buffer, stdin);
secondDigit = buffer[0];
/* Clear input stream if not empty */
if (strchr(buffer, '\n') == NULL) {
clear_stdin();
}
puts("\n");
printf("Your first digit is %c and your second digit is %c.\n",
firstDigit, secondDigit);
return 0;
}
void clear_stdin(void)
{
int c;
while ((c = getchar()) != '\n' && c != EOF) {
continue;
}
}
For the first case, fgets(firstDigit, 1, stdin); cannot read anything from the input because the buffer has a size of only 1 byte, and fgets() must store a null terminator into the destination.
For the second case: fgets(firstDigit, 2, stdin); reads 1 byte from stdin, the digit that you typed, and cannot read the newline because the destination array is already full, allowing for the null terminator. The second fgets() reads the pending newline from the first entry and returns immediately for the same reason, not letting you type the second input.
You must allow fgets() to read at least 2 bytes by providing a buffer size of at least 3:
#include <stdio.h>
int main(void) {
char firstDigit[3];
char secondDigit[3];
printf("Enter your first digit: ");
if (!fgets(firstDigit, sizeof firstDigit, stdin))
return 1;
printf("\nEnter your second digit: ");
if (!fgets(secondDigit, sizeof secondDigit, stdin))
return 1;
printf("\n\nYour first digit is %s and your second digit is %s.\n",
firstDigit, secondDigit);
return 0;
}
Note that if you type more than a single character before the enter key, the program will still behave in an unexpected way.
This is a buffer problem. When you press enter, don't know why it is saved in the stdin buffer.
After you perform an fgets(...) you must type fflush(stdin); on all circumstances.
Something like this:
printf("Enter your first digit: ");
fgets(firstDigit, 1, stdin);
fflush(stdin);

C: Clearing STDIN

basically in codeblocks for windows before each printf I have "fflush(stdin);" which works. When I copied my code to Linux, it doesn't work, nor does any of the alternatives for "fflush(stdin);" that I've found. No matter which way I seem to do it, the input doesn't seem to be clearing in the buffer or something in my code is incorrect.
#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <ctype.h>
int main()
{
char pbuffer[10], qbuffer[10], kbuffer[10];
int p=0, q=0, k=0;
int r, i, Q, count, sum;
char a[3];
a[0]='y';
while(a[0]=='y' || a[0]=='Y')
{
printf("Enter a p value: \n");
fgets(pbuffer, sizeof(pbuffer), stdin);
p = strtol(pbuffer, (char **)NULL, 10);
printf("Enter a q value: \n");
fgets(qbuffer, sizeof(qbuffer), stdin);
q = strtol(qbuffer, (char **)NULL, 10);
printf("Enter a k value: \n");
fgets(kbuffer, sizeof(kbuffer), stdin);
k = strtol(kbuffer, (char **)NULL, 10);
while(p<q+1)
{
Q=p;
sum=0;
count=0;
while(Q>0)
{
count++;
r = Q%10;
sum = sum + pow(r,k);
Q = Q/10;
}
if ( p == sum && i>1 && count==k )
{
printf("%d\n",p);
}
p++;
a[0]='z';
}
while((a[0]!='y') && (a[0]='Y') && (a[0]!='n') && (a[0]!='N'))
{
printf("Would you like to run again? (y/n) ");
fgets(a, sizeof(a), stdin);
}
}
return 0;
}
Calling fflush(stdin) is not standard, so the behavior is undefined (see this answer for more information).
Rather than calling fflush on stdin, you could call scanf, passing a format string instructing the function to read everything up to and including the newline '\n' character, like this:
scanf("%*[^\n]%1*[\n]");
The asterisk tells scanf to ignore the result.
Another problem is calling scanf to read a character into variable a with the format specifier of " %s": when the user enters a non-empty string, null terminator creates buffer overrun, causing undefined behavior (char a is a buffer of one character; string "y" has two characters - {'y', '\0'}, with the second character written past the end of the buffer). You should change a to a buffer that has several characters, and pass that limit to scanf:
char a[2];
do {
printf("Would you like to run again? (y/n) \n")
scanf("%1s", a);
} while(a[0] !='y' && a[0] !='Y' && a[0]!='n' && a[0]!='N' );
}
I think what you are trying to do is more difficult than it seems.
My interpretation of what you are trying to do is disable type ahead so that if the user types some characters while your program is processing other stuff, they don't appear at the prompt. This is actually quite difficult to do because it is an OS level function.
You could do a non blocking read on the device before printing the prompt until you get EWOULDBLOCK in errno. Or the tcsetattr function family might help. It looks like there is a way to drain input for a file descriptor in there, but it might interact badly with fgets/fscanf
A better idea is not to worry about it at all. Unix users are used to having type ahead and what you want would be unexpected behaviour for them.
Drop the need for flushing the input buffer.
OP is on the right track using fgets() rather than scanf() for input, OP should continue that approach with:
char a;
while(a !='y' && a !='Y' && a!='n' && a!='N' ) {
printf("Would you like to run again? (y/n) \n");
if (fgets(kbuffer, sizeof(kbuffer), stdin) == NULL)
Handle_EOForIOerror();
int cnt = sscanf(kbuffer, " %c", &a); // Use %c, not %s
if (cnt == 0)
continue; // Only white-space entered
}
Best to not use scanf() as it tries to handle user IO and parsing in one shot and does neither that well.
Certain present OP's woes stem from fgets() after scanf(" %s", &a); (which is UB as it should be scanf(" %c", &a);. Mixing scanf() with fgets() typically has the problem that the scanf(" %c", &a); leaves the Enter or '\n' in the input buffer obliging the code to want to flsuh the input buffer before the next fgets(). Else that fgets() gets the stale '\n' and not a new line of info.
By only using fgets() for user IO, there need for flushing is negated.
Sample fgets() wrapper
char *prompt_fgets(const char *prompt, char dest, long size) {
fputs(prompt, stdout);
char *retval = fgets(dest, size, stdin);
if (retval != NULL) {
size_t len = strlen(dest);
if (len > 1 && dest[len-1] == '\n') { // Consume trailing \n
dest[--len] = '\0';
}
else if (len + 1 == dest) { // Consume extra char
int ch;
do {
ch == fgetc(stdin);
} while (ch != '\n' && ch != EOF);
}
return retval;
}

scanf validation sits and waits for another input. Why?

I was working on this sample exercise, and everything works as I would like it to, but there is one behavior I don't understand.
When providing input: if I make consecutive invalid entries everything seems to work great. But if I enter a number different from 1,2,3 in the case of the first question, or 1,2 in the case of the second question, the program just sits there until a new input is given. If another invalid entry is made, it goes back to the error "invalid entry" message, and if an appropriate number is entered, everything moves along fine.
I do not understand why it stops to wait for a second input...anyone?
Thanks guys.
#include <stdio.h>
static int getInt(const char *prompt)
{
int value;
printf("%s",prompt);
while (scanf("%d", &value) !=1)
{
printf("Your entry is invalid.\nGive it another try: %s", prompt);
getchar();
scanf("%d", &value);
}
return value;
}
int main() {
int wood_type, table_size, table_price;
printf("Please enter " );
wood_type = getInt("1 for Pine, 2 for Oak, and 3 for Mahogany: ");
printf("Please enter ");
table_size = getInt("1 for large, 2 for small: ");
printf("\n");
switch (wood_type) {
case 1:
table_price = (table_size == 1)? 135:100;
printf("The cost of for your new table is: $%i", table_price);
break;
case 2:
table_price = (table_size == 1)? 260:225;
printf("The cost of for your new table is: $%i", table_price);
break;
case 3:
table_price = (table_size == 1)? 345:310;
printf("The cost of for your new table is: $%i", table_price);
break;
default:
table_price = 0;
printf("The cost of for your new table is: $%i", table_price);
break;
}
}
You most likely need to flush your input buffer (especially with multiple scanf calls in a function). After scanf, a newline '\n' remains in the input buffer. fflush does NOT do this, so you need to do it manually. A simple do...while loop works. Give it a try:
edit:
static int getInt(const char *prompt)
{
int value;
int c;
while (printf (prompt) && scanf("%d", &value) != 1)
{
do { c = getchar(); } while ( c != '\n' && c != EOF ); // flush input
printf ("Invalid Entry, Try Again...");
}
return value;
}
The blank line you get if you enter nothing is the normal behavior of scanf. It is waiting for input (some input). If you want your routine to immediately prompt again in the case the [Enter] key is pressed, then you need to use another routine to read stdin like (getline or fgets). getline is preferred as it returns the number of characters read (which you can test). You can then use atoi (in <stdlib.h>) to convert the string value to an integer. This will give you the flexibility you need.
example:
int newgetInt (char *prompt)
{
char *line = NULL; /* pointer to use with getline () */
ssize_t read = 0; /* number of characters read */
size_t n = 0; /* numer of chars to read, 0 no limit */
static int num = 0; /* number result */
while (printf ("\n %s ", prompt) && (read = getline (&line, &n, stdin)) != -1)
{
if ((num = atoi (line)))
break;
else
printf ("Invalid Input, Try Again...\n");
}
return num;
}
If some invalid input is entered, it stays in the input buffer.
The invalid input must be extracted before the scanf function is completed.
A better method is to get the whole line of input then work on that line.
First, put that input line into a temporary array using fgets(),
then use sscanf() (safer than scanf because it guards against overflow).
#include <stdio.h>
int main(int argc, const char * argv[]) {
char tempbuff[50];
int result, d , value;
do
{
printf("Give me a number: ");
fgets( tempbuff, sizeof(tempbuff), stdin ); //gets string, puts it into tempbuff via stdin
result = sscanf(tempbuff, "%d", &value); //result of taking buffer scanning it into value
if (result < 1){ //scanf can return 0, # of matched conversions,
//(1 in this case), or EOF.
printf("You didn't type a number!\n");
}
}while (result < 1);
//some code
return 0;
}
Knowledge from: http://www.giannistsakiris.com/2008/02/07/scanf-and-why-you-should-avoid-using-it/

Suggest an alternative for gets() function, using gcc compiler

Trying to input more than a single string in my program's strings array, for that used :
scanf("%80[^\r\n]", strings[i]);
fgets(string[i], MAXLEN, stdin);
a custom made function was also used:
int getString(char s[]) {
char ch;
int i=0;
while( (ch = getchar()) != '\n' && ch != EOF ) {
s[i] = ch;
++i;
}
s[i] = '\0';
fflush(stdin);
return i;
}
but unable to get input with more than one string each including white spaces
function gets() used to work earlier for me but since it is deprecated no alternative can be found
This is where it was used :
int getString(char s[]) {
char ch;
int i=0;
while( (ch = getchar()) != '\n' && ch != EOF ) {
s[i] = ch;
++i;
}
s[i] = '\0';
fflush(stdin);
return i;
}
struct vechileData
{
char vechileType[MAXLEN];
int begin_month;
int end_month;
double price;
} data[5];
int main(int argc, char const *argv[])
{
printf("Input Vechile data: \n");
int i=0;
while(i < 5) {
printf("Input vechile Type : \n");
fgets(data[i].vechileType, MAXLEN, stdin);
printf("Input begin month : \n");
scanf("%d", &data[i].begin_month);
printf("Input end monhth : \n");
scanf("%d", &data[i].end_month);
printf("Input price : \n");
scanf("%lf", &data[i].price);
++i;
}
printf("Input Vechile Type to display information about the vechile : \n");
char vech[MAXLEN];
fgets(vech, MAXLEN, stdin);
i=0;
while(i < 5) {
if (strcmp(vech,data[i].vechileType) == 0)
{
printf("vechileType: %s\n", data[i].vechileType);
printf("Begin month: %d\n", data[i].begin_month);
printf("End month: %d\n", data[i].end_month);
printf("Price : %lf\n", data[i].price);
}
++i;
}
return 0;
}
It skips the next input to string statement during run time, "seems to"
Your problem is really not a gets() issue.
None of the scanf("%d", ...) and scanf("%lf", ...) consume the '\n' after the number and thus contribute to your issue. It is the next read of stdin to take in the '\n'. So when the next car type is read, it gets the lingering '\n'. Your 2nd car type ends up being "\n".
Use of fgets(data[i].vechileType, MAXLEN, stdin); puts a '\n' in data[i].vechileType. You likely do not want this. Your former use of gets() consumed, but did not put the '\n' in its return.
I long ago gave up doing user input with scanf() due to these subtle issues.
Recommend to separate input from parsing, use fgets() and then sscanf(). Example:
char number[80];
if (fgets(number, sizeof(number), stdin)) {
sscanf(number, "%d", &x)
Your implementation of a gets() replacement differs as follows
1) It does not return s (or NULL or error/eof).
2) It does not set eof indicator on eof.
3) Should getchar() return a '\0', your while loop errantly continues.
Recommend that if you must replace gets(), do so via fgets().
#define My_gets_N (1024 /* Some BA number */)
char *My_gets(char * str) {
char buffer[My_gets_N];
char *retval = fgets(buffer, sizeof(My_gets_N), stdin);
if (retval) {
int l = strlen(buffer);
/* fgets() saves '\n', but gets() does not */
if ((l > 0) && (buffer[l-1] == '\n')) {
l--;
}
memcpy(str, buffer, l);
str[l] = '\0';
return str;
}
else {
return 0;
}
}
If you replacement solution needs to deal with string length > the fixed My_gets_N, other coding is needed.
You must be more specific about what went wrong with the fgets() approach, that's the one I would recommend and it does work.
Note that fgets() will input the entire line, including linefeed/carriage returns at the end, so you might need to clean those off if they're undesirable to keep.
I don't understand how gets() worked for you, despite the warning that practically every C book post K&R gives, as it's not only deprecated, but extremely dangerous to use. Like the others have said, fgets() would definitely work if you used it correctly.
Instead of replacing all the instances of uses of gets with fgets. Use following Macros:
#define TRUNCATE_NULL(strText) \
 { \
   int _strlen = strlen(strText); \
   if (_strlen > 0 && strText[_strlen - 1] == '\n') strText[_strlen - 1] = '\0'; \
   else while(fgetc(stdin)!='\n'); \
 }
#define gets(strText) fgets(strText, sizeof(strText), stdin); TRUNCATE_NULL(strText);
Why use fgets?
Because it is more secure than gets.
Is gets really insecure?
Yes. It is greedy indeed, it will accept as much food as you give, even if it can not eat.
So technically, as #halfer rightly commented below,
with the use of gets, program is prone to buffer overflow.
How ?
char name[5];
gets(name);
Now provide input of more than 5 characters, it will accept it. This would overwrite data from memory, which should not be overwritten this way.
Ok with fgets, but why use TRUNCATE_NULL macro ?
fgets is not perfect either. it will accept \n (Enter) as character to be placed in input name.So to remove unnecessary \n, and to make sure expected functionality of gets is achieved we can use it.
Actually, there you can use while((getchar())!='\n'); to avoid such type of problem and one thing there is no need to use of fflush(stdin) function.
Here's code you can use
#include<stdio.h>
#include<string.h>
#define MAXLEN 50
int getString(char s[])
{
char ch;
int i=0;
while( (ch = getchar()) != '\n' && ch != EOF )
{
s[i] = ch;
++i;
}
s[i] = '\0';
return i;
}
struct vechileData
{
char vechileType[MAXLEN];
int begin_month;
int end_month;
double price;
}data[5];
int main(int argc, char const *argv[])
{
printf("Input Vechile data: \n");
int i=0;
while(i < 2)
{
printf("Input vechile Type : \n");
fgets(data[i].vechileType, MAXLEN, stdin);
printf("Input begin month : \n");
scanf("%d", &data[i].begin_month);
printf("Input end monhth : \n");
scanf("%d", &data[i].end_month);
printf("Input price : \n");
scanf("%lf", &data[i].price);
while((getchar())!='\n');
++i;
}
printf("Input Vechile Type to display information about the vechile : \n");
char vech[MAXLEN];
fgets(vech, MAXLEN, stdin);
i=0;
while(i < 2)
{
if (strcmp(vech,data[i].vechileType) == 0)
{
printf("vechileType: %s\n", data[i].vechileType);
printf("Begin month: %d\n", data[i].begin_month);
printf("End month: %d\n", data[i].end_month);
printf("Price : %lf\n", data[i].price);
}
++i;
}
return 0;
}
I hope this will help you.....

Resources