【问题标题】:How to consume Unix or Windows style newline [duplicate]如何使用 Unix 或 Windows 样式的换行符 [重复]
【发布时间】:2021-12-11 02:13:19
【问题描述】:

我正在阅读 stdin,有时会有 unix 风格的换行符,有时还有 windows 风格的换行符。

如何使用这两种换行符?

【问题讨论】:

标签: c


【解决方案1】:

假设你知道会有换行符,解决办法是消耗一个字符,然后决定:

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

【讨论】:

    【解决方案2】:

    还有更多newline conventions。特别是,所有四个涉及 CR \r 和 LF \n -- \n\r\r\n\n\r -- 实际上是在野外遇到的。

    为了读取文本输入,可能是交互式的,并同时支持所有这四种换行编码,我建议使用类似以下的辅助函数:

    #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 = '\0';
        }
    
        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 = '\0';
                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] = '\0';
        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 = '\0';
    
        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 字符 (\0) 经常会让人头疼,因此让函数完全跳过这些字符通常是一种稳健的方法。

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

    上面的代码 sn-p 包含一个main(),它读取并显示输入行,使用当前语言环境来表示“非空白控制字符”(!isspace(c) &amp;&amp; iscntrl(c))的含义。

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

    问题?

    【讨论】:

      猜你喜欢
      • 2016-12-31
      • 2015-02-18
      • 2010-10-17
      • 2012-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-25
      相关资源
      最近更新 更多