Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Removing trailing newline character from fgets() input

Perhaps the simplest solution uses one of my favorite little-known functions, strcspn():

buffer[strcspn(buffer, "\n")] = 0;

If you want it to also handle '\r' (say, if the stream is binary):

buffer[strcspn(buffer, "\r\n")] = 0; // works for LF, CR, CRLF, LFCR, ...

The function counts the number of characters until it hits a '\r' or a '\n' (in other words, it finds the first '\r' or '\n'). If it doesn't hit anything, it stops at the '\0' (returning the length of the string).

Note that this works fine even if there is no newline, because strcspn stops at a '\0'. In that case, the entire line is simply replacing '\0' with '\0'.


The elegant way:

Name[strcspn(Name, "\n")] = 0;

The slightly ugly way:

char *pos;
if ((pos=strchr(Name, '\n')) != NULL)
    *pos = '\0';
else
    /* input too long for buffer, flag error */

The slightly strange way:

strtok(Name, "\n");

Note that the strtok function doesn't work as expected if the user enters an empty string (i.e. presses only Enter). It leaves the \n character intact.

There are others as well, of course.


size_t ln = strlen(name) - 1;
if (*name && name[ln] == '\n') 
    name[ln] = '\0';

Below is a fast approach to remove a potential '\n' from a string saved by fgets().
It uses strlen(), with 2 tests.

char buffer[100];
if (fgets(buffer, sizeof buffer, stdin) != NULL) {

  size_t len = strlen(buffer);
  if (len > 0 && buffer[len-1] == '\n') {
    buffer[--len] = '\0';
  }

Now use buffer and len as needed.

This method has the side benefit of a len value for subsequent code. It can be easily faster than strchr(Name, '\n'). Ref YMMV, but both methods work.


buffer, from the original fgets() will not contain in "\n" under some circumstances:
A) The line was too long for buffer so only char preceding the '\n' is saved in buffer. The unread characters remain in the stream.
B) The last line in the file did not end with a '\n'.

If input has embedded null characters '\0' in it somewhere, the length reported by strlen() will not include the '\n' location.


Some other answers' issues:

  1. strtok(buffer, "\n"); fails to remove the '\n' when buffer is "\n". From this answer - amended after this answer to warn of this limitation.

  2. The following fails on rare occasions when the first char read by fgets() is '\0'. This happens when input begins with an embedded '\0'. Then buffer[len -1] becomes buffer[SIZE_MAX] accessing memory certainly outside the legitimate range of buffer. Something a hacker may try or found in foolishly reading UTF16 text files. This was the state of an answer when this answer was written. Later a non-OP edited it to include code like this answer's check for "".

    size_t len = strlen(buffer);
    if (buffer[len - 1] == '\n') {  // FAILS when len == 0
      buffer[len -1] = '\0';
    }
    
  3. sprintf(buffer,"%s",buffer); is undefined behavior: Ref. Further, it does not save any leading, separating or trailing whitespace. Now deleted.

  4. [Edit due to good later answer] There are no problems with the 1 liner buffer[strcspn(buffer, "\n")] = 0; other than performance as compared to the strlen() approach. Performance in trimming is usually not an issue given code is doing I/O - a black hole of CPU time. Should following code need the string's length or is highly performance conscious, use this strlen() approach. Else the strcspn() is a fine alternative.