【发布时间】:2014-05-01 08:29:00
【问题描述】:
编辑
问题的解决方案是了解 Ctrl-D 实际在做什么。
在新的空行上,单个 Ctrl-D 将发出 EOF 信号。
但如果该行中已经有字符,第一个 Ctrl-D 会使该行的内容回显到屏幕上(但不会写入STDOUT)。如果字符已经在缓冲区中,则必须发出第二个 Ctrl-D 来发出信号 EOF,从而将缓冲区写入 STDOUT。
这可以通过将输出重定向到文件来证明。
EDIT
我正在使用 fgetc() 从stdin 读取输入。我循环直到收到 EOF。在循环中,我根据在按下 Ctrl-D 之前键入的字符构建一个字符串。但我无法找出退出循环的方法,因为从中读取的缓冲区 ch = fgetc() 不包含 EOF。 (EOF 只触发 fgetc() 返回它的第一个值。)
ungetc() 不允许将 EOF 推入缓冲区,推入任何其他字符都有与真实数据混淆的风险,我被卡住了!我已经阅读了很多答案,但它们没有解决这个问题或不适用于我试图实现的用例。
我希望能够对标准输入缓冲区进行计数、查看等操作。
我真的不想读取一整行(或一次读取 X 个字符),因为我正在处理从 fgetc() 到达(edit)的每个字符。
关于如何克服这一困境的任何建议? (不使用 NCurses)
我正在使用 Ubuntu。 EOF = Ctrl-D 这是我正在使用的一些代码:
这行得通,和乔纳森的简单例子一样,但不是我想要的:
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;
}
但是,这被卡住了,但正在尝试做我想做的事情(edit),但需要再次使用 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;
}
【问题讨论】:
-
您使用的是哪个操作系统?
-
如果您打算检查
EOF,请确保ch是int。 -
通常在 Linux 中,键入一些字符后按 Ctrl-D 会导致输入流被刷新(因此您的程序可以开始读取它)但不会结束输入。再次按下它,或者在换行符之后直接按下它,会导致流结束。
-
@MattMcNabb:
setvbuf是标准 C 的一部分,如果stdin是例如 FIFO,它将完美地工作。至少对于 Unix 系统,如果stdin是一个 tty 则它将不起作用,但 C 标准既不要求也不禁止这样做;不是因为标准输入是行缓冲的,而是因为默认情况下,tty 设备本身在输入 ENTER 或某些其他特殊字符之前不会向用户区返回任何内容。 -
好的;您需要在调用代码中的
malloc()之后添加*thisLine = '\0';;否则,您阅读的内容越界。你也像筛子一样漏水;你需要在strconcat()中free(str1);。此外,如果内存分配失败,您的代码会因为读取 NULL 指针而崩溃和烧毁。您应该考虑每次都使用realloc()而不是malloc();随着时间的推移,它将更具成本效益。