有三种相对常见的换行约定:\r\n、\n 和 \r,当编辑者对换行约定感到困惑时,可能会出现第四种换行约定 \n\r。如果一种方法支持通用换行符,它同时支持所有四个,即使是固定的。
使用通用换行支持逐行读取文件很容易。唯一的问题是来自行缓冲源的交互式输入看起来像是晚了一行。为了避免这种情况,可以将行读入动态缓冲区,但不包括换行符;并在阅读下一行时使用换行符。例如:
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
ssize_t getline_universal(char **dataptr, size_t *sizeptr, FILE *in)
{
char *data = NULL;
size_t size = 0;
size_t used = 0;
int c;
if (!dataptr || !sizeptr || !in) {
errno = EINVAL;
return -1;
}
if (*sizeptr) {
data = *dataptr;
size = *sizeptr;
} else {
*dataptr = data;
*sizeptr = size;
}
/* Ensure there are at least 2 chars available. */
if (size < 2) {
size = 2;
data = malloc(size);
if (!data) {
errno = ENOMEM;
return -1;
}
*dataptr = data;
*sizeptr = size;
}
/* Consume leading newline. */
c = fgetc(in);
if (c == '\n') {
c = fgetc(in);
if (c == '\r')
c = fgetc(in);
} else
if (c == '\r') {
c = fgetc(in);
if (c == '\n')
c = fgetc(in);
}
/* No more data? */
if (c == EOF) {
data[used] = '\0';
errno = 0;
return -1;
}
while (c != '\n' && c != '\r' && c != EOF) {
if (used + 1 >= size) {
if (used < 7)
size = 8;
else
if (used < 1048576)
size = (3 * used) / 2;
else
size = (used | 1048575) + 1048577;
data = realloc(data, size);
if (!data) {
errno = ENOMEM;
return -1;
}
*dataptr = data;
*sizeptr = size;
}
data[used++] = c;
c = fgetc(in);
}
/* Terminate line. We know used < size. */
data[used] = '\0';
/* Do not consume the newline separator. */
if (c != EOF)
ungetc(c, in);
/* Done. */
errno = 0;
return used;
}
上述函数的工作方式与 POSIX.1-2008 getline() 非常相似,不同之处在于它支持所有四种换行约定(甚至是混合的),并且它在读取的行中省略了换行符。 (也就是说,换行符不包含在返回值或动态分配的缓冲区中。换行符留在流中,供下一个getline_universal() 操作使用。)
与标准函数不同,getline_universal() 总是将 errno: 设置为零,如果成功,否则非零。如果您不喜欢这种行为,请随时更改。
作为一个用例示例:
int main(void)
{
unsigned long linenum = 0u;
char *line_buf = NULL;
size_t line_max = 0;
ssize_t line_len;
while (1) {
line_len = getline_universal(&line_buf, &line_max, stdin);
if (line_len < 0)
break;
linenum++;
printf("%lu: \"%s\" (%zd chars)\n", linenum, line_buf, line_len);
fflush(stdout);
}
if (errno) {
fprintf(stderr, "Error reading from standard input: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Not necessary before exiting, but here's how to
safely discard the line buffer: */
free(line_buf);
line_buf = NULL;
line_max = 0;
line_len = 0;
return EXIT_SUCCESS;
}
请注意,由于free(NULL) 是安全的,您可以在调用getline_universal(&line_buf, &line_max, stream) 之前丢弃缓冲区(使用free(line_buf); line_buf = NULL; line_max = 0;)。