amarVashishth
amarVashishth

Reputation: 877

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"

Upvotes: 3

Views: 34029

Answers (5)

Mani Shankar
Mani Shankar

Reputation: 1

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.....

Upvotes: 0

prashant
prashant

Reputation: 1484

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);
  1. Why use fgets?

    Because it is more secure than gets.

  2. 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.

  3. 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.

  4. 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.

Upvotes: 0

chux
chux

Reputation: 154075

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.

Upvotes: 7

user2030052
user2030052

Reputation:

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.

Upvotes: 1

unwind
unwind

Reputation: 400009

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.

Upvotes: 2

Related Questions