Mathue24
Mathue24

Reputation: 178

Alternative to gets?

I used to use gets but then I heard that it got removed from c11 and that its overall very dangerous. So I did some searching and found out that you can use fgets() to do the same thing.

The problems is that when I do use fgets() it seems to also copy the end of the line aswell, which ends up making an extra unwanted line.

To show you what I mean:

//if I have 
char key[30];
fgets(key, sizeof(key), stdin);

//now if ender for instance: Doggo and do:
printf("The key is:%s|hozaah!\n", key);

//I expect it to print:
The key is:Doggo|hozaah!

//Instead it prints:
The key is:Doggo
|hozaah!

Is there a way to get around this? Or is there another function I can use instead?

Upvotes: 4

Views: 13722

Answers (2)

chqrlie
chqrlie

Reputation: 144715

There is no standard direct replacement for gets(), but there are many easy ways to get rid of the newline if present:

  • The simplest using strcspn (declared in <string.h>):

    if (fgets(buf, sizeof buf, fp)) {
        buf[strcspn(buf, "\n")] = '\0';
    }
    
  • The classic using strlen:

    if (fgets(buf, sizeof buf, fp)) {
        size_t len = strlen(buf);
        if (len > 0 && buf[len - 1] == '\n')
            buf[--len] = '\0';
    }
    
  • Another classic with strchr:

    if (fgets(buf, sizeof buf, fp)) {
        char *p = strchr(buf, '\n');
        if (p != NULL)
            *p = '\0';
    }
    

An alternative is the POSIX function getline() which might be available on your system:

#include <stdio.h>
ssize_t getline(char **lineptr, size_t *n, FILE *stream);

The buffer is allocated or reallocated with malloc() and its size is updated into *n. The initial values should be lineptr = NULL and n = 0.

Upvotes: 4

Kerrek SB
Kerrek SB

Reputation: 477030

The answer lies in the fact that fgets reads as many characters as fit into the buffer, including a null terminator, but only up to end of file or a newline, and it will store the newline in the string, too: man 3 fgets

So if you enter input on the terminal, the newline is transmitted to the standard input, too, and read by fgets. This is different from the old gets, which replaces the newline with a null byte.

So if you're reading lines and don't want the potential newline, strip it out. Unfortunately, fgets does not tell you how many characters were read, so you have to scan the string again to work it out:

char* p = fgets(key, sizeof(key), stdin);

if (!p) { /* error */ }

size_t n = strlen(key);

if (n > 0 && key[n - 1] == '\n') {
  key[n - 1] = '\0';
} else {
  /* buffer too short or EOF reached without newline */
}

printf("The line was: '%s'\n", key);

It may be worth considering alternatives: If you only need one input, don't input the newline in the first place. If you need lots of lines, use fread into a fixed-size buffer and scan for newlines yourself.

Upvotes: 1

Related Questions