如何使用 Unix 或 Windows 样式换行符



我正在阅读stdin,有时有 unix 风格的换行符,有时是 Windows 风格的换行符。

如何使用任一类型的换行符?

假设你知道会有一个换行符,解决方案是使用一个字符,然后决定:

10 - LF ... Unix style newline
13 - CR ... Windows style newline

如果是13,则必须再消耗一个字符(10)

const char x = fgetc(stdin); // Consume LF or CR
if (x == 13) fgetc(stdin); // consume LF

还有比这更多的换行约定。特别是涉及CRr和LFn的四种动物——nrrnnr——实际上都是在野外饲养的。

为了读取文本输入,可能是交互式的,并同时支持所有这四个换行符编码,我建议使用如下所示的帮助程序函数:

#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
size_t get_line(char **const lineptr, size_t *const sizeptr, char *const lastptr, FILE *const in)
{
char  *line;
size_t size, have;
int    c;
if (!lineptr || !sizeptr || !in) {
errno = EINVAL; /* Invalid parameters! */
return 0;
}
if (*lineptr) {
line = *lineptr;
size = *sizeptr;
} else {
line = NULL;
size = 0;
}
have = 0;
if (lastptr) {
if (*lastptr == 'n') {
c = getc(in);
if (c != 'r' && c != EOF)
ungetc(c, in);
} else
if (*lastptr == 'r') {
c = getc(in);
if (c != 'n' && c != EOF)
ungetc(c, in);
}
*lastptr = '';
}
while (1) {
if (have + 2 >= size) {
/* Reallocation policy; my personal quirk here.
* You can replace this with e.g. have + 128,
* or (have + 2)*3/2 or whatever you prefer. */ 
size = (have | 127) + 129;
line = realloc(line, size);
if (!line) {
errno = ENOMEM; /* Out of memory */
return 0;
}
*lineptr = line;
*sizeptr = size;
}
c = getc(in);
if (c == EOF) {
if (lastptr)
*lastptr = '';
break;
} else
if (c == 'n') {
if (lastptr)
*lastptr = c;
else {
c = getc(in);
if (c != EOF && c != 'r')
ungetc(c, in);
}
break;
} else
if (c == 'r') {
if (lastptr)
*lastptr = c;
else {
c = getc(in);
if (c != EOF && c != 'n')
ungetc(c, in);
}
break;
}            
if (iscntrl(c) && !isspace(c))
continue;
line[have++] = c;
}
if (ferror(in)) {
errno = EIO; /* I/O error */
return 0;
}
line[have] = '';
errno = 0; /* No errors, even if have were 0 */
return have;
}
int main(void)
{
char   *data = NULL;
size_t  size = 0;
size_t  len;
char    last = '';
setlocale(LC_ALL, "");
while (1) {
len = get_line(&data, &size, &last, stdin);
if (errno) {
fprintf(stderr, "Error reading standard input: %s.n", strerror(errno));
return EXIT_FAILURE;
}
if (!len && feof(stdin))
break;
printf("Read %lu characters: '%s'n", (unsigned long)len, data);
}
free(data);
data = NULL;
size = 0;
return EXIT_SUCCESS;
}

除了我使用的errno常量(EINVALENOMEMEIO),上面的代码是C89,应该是可移植的。

get_line()函数在必要时动态重新分配行缓冲区,使其足够长。对于交互式输入,您必须在遇到的第一个换行符处接受换行符(因为如果第一个字符恰好是唯一的换行符,则尝试读取第二个字符会阻塞)。如果指定,则lastptr处的单字符状态用于检测和正确处理下一行读取开头的任何双字符换行符。如果未指定,该函数将尝试将整个换行符用作当前行的一部分(这对于非交互式输入,尤其是文件)是可以的。

换行符不存储或计入行长度。为了增加易用性,该函数还会跳过非空格控制字符。特别是嵌入的NUL字符()经常引起头痛,所以让函数完全跳过这些通常是一个稳健的方法。

最后,该函数始终将errno-- 设置为零(如果未发生错误),否则--,包括ferror()情况,否则将非零错误代码设置为零,因此检测错误条件是微不足道的。

上面的代码片段包括一个main(),它读取并显示输入行,使用当前语言环境表示"非空格控制字符"(!isspace(c) && iscntrl(c))。

虽然这绝对不是读取输入的最快机制,但它并没有那么慢,而且是一个非常强大的机制。

问题?

最新更新