Before we discuss performance, it is important to verify correctness.
Let's look at your methods, and some other popular ones:
strchr
if (fgets(sentence, 11, stdin) != NULL) {
p = strchr(sentence, '\n');
*p = '\0';
}
You forgot to test if p
is not NULL
. This is a big problem because it is possible that sentence
does not contain a \n
, either because the line read was loo long and only part of it is in sentence
or for the last line in the file if it is not terminated by a \n
, or if the file contains a null byte. You should write this version this way:
if (fgets(sentence, 11, stdin) != NULL) {
char *p = strchr(sentence, '\n');
if (p != NULL)
*p = '\0';
...
}
strchrnul
Some C libraries have a non standard function strchrnul
with this prototype:
char *strchrnul(const char *s, int c);
It returns a pointer to the first occurrence of c
in the string s
or a pointer to the final \0
if no occurrence can be found. This functions allows for a very simple and efficient way to strip the \n
:
if (fgets(sentence, 11, stdin) != NULL) {
*strchrnul(sentence, '\n') = '\0';
...
}
The only drawback is that this function is not part of the C Standard and may not be available on some platforms.
strtok
if (fgets(sentence, 11, stdin) != NULL) {
token = strtok(sentence, "\n");
...
}
This version is incorrect: strtok
has a side effect on its internal data. This version will interfere with surrounding code that uses strtok
. If you bury this method in a function, you will hide this side effect and may cause hard to find bugs for programmers using you function. You could possibly use the reentrant version of strtok
: strtok_r
, but it is not always available.
Furthermore, as user3121023 commented, strtok
will not remove the \n
if it is at the beginning of the string. This definitely disqualifies this method. (strtok
has too many quirks, it should probably be avoided altogether anyway.)
strlen
You did not mention the strlen
alternative. I see it quite often written this way:
if (fgets(sentence, 11, stdin) != NULL) {
sentence[strlen(sentence) - 1] = '\0';
...
}
This is incorrect for multiple reasons:
sentence
might not have a \n
as its last character, as already explained for the strchr
version. Attempting to remove the \n
this way would remove a valid character.
sentence
might be an empty string, in which case the code would have undefined behavior. For sentence
to be empty requires exceptional conditions not specified in the C Standard: if the input stream contains a NUL byte at the beginning of a line, fgets()
might return an empty buffer.
For correctness, this method should be implemented this way:
if (fgets(sentence, 11, stdin) != NULL) {
size_t len = strlen(sentence);
if (len > 0 && sentence[len - 1] == '\n')
sentence[--len] = '\0';
// useful side effect: len has been updated.
...
}
strcspn
if (fgets(sentence, 11, stdin) != NULL) {
sentence[strcspn(sentence, "\n")] = '\0';
...
}
This is the simplest version. It works whether sentence
contains a \n
or not and even for an empty string. It is less likely to be misused by a programmer.
Performance
Whether strcspn
is more or less efficient than the other ones depends a lot on the C library implementation and the compiler performance. The performance should be better than that of strtok
, since it does only one scan. It is likely less efficient than strchr
and even less than strlen
, but for correctness, the strlen
alternative should also use 2 extra tests for len > 0
and sentence[len - 1] == '\n'
, reducing performance.
Note that some libraries use compile-time tests that may allow special casing a 1 byte string literal argument. In this case, the compiler can generate inline code that would be even more efficient than strchr
.
It is true that strcspn
may be implemented in a rather naive way in some C libraries, but this is not the case for the GNU libc, nor for the Apple C Library. Their implementations are quite efficient, and at least gcc
uses builtin knowledge of this and other string functions to generate better code.
On environments with an optimized implementation of strchrnul
, this method should be hard to beat.
As always, run benchmarks and profile the different alternatives with different compilers, processors, C libraries... different platforms may yield very different results.