无法使用STDIN在fgetc()缓冲区中找到EOF

时间:2014-03-24 05:25:16

标签: c eof fgetc

修改
解决问题的方法是了解Ctrl-D实际上在做什么 在一个新的空行上,一个Ctrl-D将发出EOF信号 但是如果行中已有字符,则第一个Ctrl-D会使行的内容回显到屏幕(但不会写入STDOUT)。如果缓冲区中已存在字符,则必须发出第二个Ctrl-D信号EOF,从而将缓冲区写入STDOUT。 这可以通过将输出重定向到文件来证明 的修改

我正在使用fgetc()来读取stdin的输入。我循环直到收到EOF。在循环中,我根据按下Ctrl-D之前键入的字符构建一个字符串。但我无法想出退出循环的方法,因为缓冲区ch = fgetc()读取不包含EOF。 (EOF仅触发fgetc()返回其第一个值。)

ungetc()不允许将EOF推入缓冲区,推动任何其他char运行与真实数据混淆的风险,我被卡住了!我已经阅读了很多答案,但他们没有解决这个问题,或者不适用于我正在尝试实现的用例。

我希望能够在stdin缓冲区上计算,查看等等。

我真的不想读整行(或者一次是X个字符),因为我正在处理来自fgetc()的每个字符(编辑)。

有关如何克服这种困境的任何建议? (不使用NCurses)

我正在使用Ubuntu。 EOF = Ctrl-D 以下是我正在使用的一些代码:

这是有效的,并且与Jonathan的简单示例相同,但不是我想要的:

int main(int argc, char **argv) {

    int inputChr;

    do {
        inputChr = fgetc(stdin);
        if (inputChr != EOF) {
            fputc( inputChr, stdout);
        }
        if (feof(stdin)) {
            if (ferror(stdin)) {
                perror(NULL);
                return errno;
            }
        }
    } while (inputChr != EOF);
    return EXIT_SUCCESS;
}

然而,这种情况越来越困难,但正在尝试做我想做的事情(编辑),但第二次需要Ctrl-D:

char *buildLine (FILE *inputSource, char *currLine, int showTabs, int showNonPrint, int *haveLF) {

    int inputChr;
    char *thisLine = malloc(1);
    int inputSize;

    *haveLF = FALSE;
    while ( (inputChr = fgetc(inputSource)) != EOF ) {

        if (ferror(inputSource)) {
            perror(NULL);
        } else {
            if (inputChr == LF) {
                *haveLF = TRUE;
            } else {
                thisLine = strconcat(thisLine,(char *)&inputChr);
            }
        }
    }

    return thisLine;
}

有人问过更多的代码:

char * strconcat ( char *str1, char * str2) {

    char *newStr = malloc(strlen(str1)+strlen(str2)+1);
    if (newStr == NULL) {
        return NULL;
    }
    strcpy(newStr,str1);
    strcat(newStr,str2);

    return newStr;
}

以下版本按字符处理输入字符,其工作方式与cat类似。但我决定在应用一些我需要实现的额外转换之前先将每个字符处理成一行。这简化了状态机设计,但是尝试构建线路并不是一个好选择(不使用NCurses)。 :(

int echoInput( FILE *inputSource, FILE *outputDestination, int numbers, int showEnds) {

    int haveNewLine = TRUE;
    int lineNo = 1;
    int inputChr;

    do {
        inputChr = fgetc(inputSource);
        if (inputChr != EOF) {
            if (numbers && haveNewLine) {
                long lineNoSize = (long) log10(lineNo)+1;   // effectively floor(log10(lineNo)+1) = number digits
                char *lineNoStr =  (lineNoSize<6)?malloc(8):malloc(lineNoSize+2);   // If less than 6 digits, allow for minimum 6 plus tab.  Also +1 for terminator.
                if (lineNoStr == NULL) {
                    printf ("Error::Out of Memory");
                    return ENOMEM;
                }
            sprintf(lineNoStr,"%6d\t",lineNo);  // format lineNo string
                fputs(lineNoStr, outputDestination);    // send string to output
                lineNo++;
                haveNewLine = FALSE;
            }
            if (inputChr == LF) {
                if (showEnds) {
                    fputc('$', outputDestination);  // send char to output
                }
                haveNewLine = TRUE;
            }
            fputc( inputChr, outputDestination);
        }
        if (feof(inputSource)) {
            if (ferror(inputSource)) {
                perror(NULL);
                return errno;
            }
        }
        if (ferror(outputDestination)) {
            perror(NULL);
            return errno;
        }
    } while (inputChr != EOF);
    return EXIT_SUCCESS;
}

2 个答案:

答案 0 :(得分:3)

这个问题必须有其他变体,并且有足够好的答案,但这里还有一个。

fgetc()(以及getc()getchar())返回的值为int,而不是char。它必须是int,因为可以返回的值集包括char的每个可能值和一个额外值EOF,它是负数(而字符都是正数)。尽管EOF最常见-1,但您永远不应该对此假设进行编码。

有两件事可能出错:

char c;

while ((c = fgetc(stdin)) != EOF)

如果类型char已签名,则某些字符(通常为0xFF,通常为ÿ,y-umlaut,Unicode U + 00FF,带有DIAERESIS的LATIN SMALL LETTER)将被误解为在EOF之前指示EOF达到。

如果类型char是无符号的,那么您将永远不会检测到EOF,因为分配给c的值将为0xFF(正),并且永远不会比较等于EOF(负值)

你是不对的,你不能用ungetc()将EOF推回输入流。

请注意 Control-D (或Windows上的 Control-Z )不会在输入队列中添加字符。相反,它表示没有更多可用字符(稍微简化一些事情),这意味着read()系统调用返回0字节读取,这意味着EOF。

使用getchar()putchar()将标准输入复制到标准输出的简单程序是:

int c;
while ((c = getchar()) != EOF)
    putchar(c);

如果您希望打开文件并阅读这些内容,则可以对其进行调整以使用fgetc()getc()fputc()putc()。关键是使用int来保存读取的值。

答案 1 :(得分:0)

EOF通常是一个整数(不是char),它与任何有效字符的值不同。

正常C样式将终止使用\0构建的字符串。理论上,理论上可以读取NUL字符,如果你想处理这些可能性,你需要记录读取的字符数以及读取的缓冲区数成。