Implementing your own safe gets()
function using getchar_unlocked()
is easy and reasonably efficient.
If your application is so performance sensitive, that you think fgets()
and removing the scan is going to be the bottleneck, you should probably not use the stream functions at all and use lower level read()
system calls or memory mapped files.
In any case, you should carefully benchmark your application and use profiling tools to determine where the time is spent.
Here is a simple implementation that returns the line length but truncates the line to whatever fits in the destination array buf
of length n
and returns EOF
at end of file:
int my_gets(char *buf, size_t n) {
int c;
size_t i = 0;
while ((c = getchar_unlocked()) != EOF && c != '\n') {
if (i < n) {
buf[i] = c;
}
i++;
}
if (i < n) {
buf[i] = '\0';
} else
if (n > 0) {
buf[n - 1] = '\0';
}
if (c == EOF && i == 0) {
return EOF;
} else {
return (int)i;
}
}
If your goal is to parse a log file line by line and only this function to read from stdin
, you can implement a custom buffering scheme with read
or fread
in a custom version of gets()
. This would be portable and fast but not thread safe nor elegant.
Here is an example that is 20% faster than fgets()
on my system:
/* read a line from stdin
strip extra characters and the newline
return the number of characters before the newline, possibly >= n
return EOF at end of file
*/
static char gets_buffer[65536];
static size_t gets_pos, gets_end;
int my_fast_gets(char *buf, size_t n) {
size_t pos = 0;
for (;;) {
char *p = gets_buffer + gets_pos;
size_t len = gets_end - gets_pos;
char *q = memchr(p, '\n', len);
if (q != NULL) {
len = q - p;
}
if (pos + len < n) {
memcpy(buf + pos, p, len);
buf[pos + len] = '\0';
} else
if (pos < n) {
memcpy(buf + pos, p, n - pos - 1);
buf[n - 1] = '\0';
}
pos += len;
gets_pos += len;
if (q != NULL) {
gets_pos += 1;
return (int)pos;
}
gets_pos = 0;
gets_end = fread(gets_buffer, 1, sizeof gets_buffer, stdin);
if (gets_end == 0) {
return pos == 0 ? EOF : (int)pos;
}
}
}