Reputation: 144595
fgets()
is a safe function to read a line of input, but it stores the new line byte '\n'
read from the file in the array if it fits.
In many if not most cases, this new line must be removed before further processing the line contents.
Several simple methods can be used for that, but I have seen a compact and tricky proposal:
strtok(line, "\n"); // strip the newline
Why is this method incorrect and why does it not always work?
Upvotes: 3
Views: 789
Reputation: 144595
The method is tricky as the strtok()
function has side effects on a global hidden state variable. This may affect surrounding code and prove hard to debug.
Furthermore, there is a simple case where strtok(line, "\n")
will not overwrite the '\n'
with a null byte: If the line read by fgets()
is an empty line containing only a single new line byte. For this contents, strtok()
will skip the initial new line, searching for a different character, which is not present, and return NULL
not modifying the array. Hence it will not strip the new line.
This is a compelling reason to not use strtok(line, "\n")
to strip the new line byte.
Of course one can fix this issue by writing:
if (*line == '\n')
*line = '\0';
else
strtok(line, "\n");
Or cumbersome one-liners:
(void)(*line == '\n' ? (*line = '\0') : (strtok(line, "\n"), 0);
if (!strtok(line, "\n")) *line = '\0';
(void)(strtok(line, "\n") || (*line = '\0'));
But the code is no longer compact and still has other side effects.
Other methods are available:
using an explicit for
statement:
for (char *p = line; *p; p++) {
if (*p == '\n')
*p = '\0';
}
using strlen()
:
size_t len = strlen(line);
if (len > 1 && line[len - 1] == '\n') {
line[--len] = '\0';
}
// len is the length if the stripped line
using strchr()
:
char *p = strchr(line, '\n');
if (p) {
*p = '\0';
}
using strcspn()
in a one-liner:
line[strcspn(line, "\n")] = '\0'; // strip the newline if any.
Upvotes: 6