为何Select在输入文件中始终返回0?求因与解决方案
select检查FILE*可读失败的原因及解决方案
问题根源
你的测试失败主要有两个核心原因:
select的nfds参数设置错误
select的第一个参数要求是所有待监视文件描述符中的最大值加1,你当前固定将nfds设为1,但tmpfile()创建的临时文件对应的文件描述符通常是3(stdin=0、stdout=1、stderr=2),远大于1。这会导致select直接忽略这个文件描述符的状态,返回0,最终函数返回-1触发断言失败。select不适合用于普通磁盘文件
select是为套接字、管道、终端等“阻塞型I/O设备”设计的,这类设备可能需要等待数据到达。但对于普通磁盘文件,只要文件指针未到达EOF,select会始终返回“可读”——因为磁盘文件的I/O是即时就绪的,用它来检查普通文件是否有数据可读没有实际意义。
修复方案
方案1:修正select的用法(仅适用于非普通文件场景)
如果你的函数需要支持套接字、管道等场景,先修正nfds的设置,同时加上FD_ISSET确认目标fd是否就绪:
int ioManager_nextReady(FILE *IFILE) { int inDescrp = fileno(IFILE ? IFILE : stdin); struct timeval timeout; timeout.tv_sec = timeout.tv_usec = 0; fd_set readfds; FD_ZERO(&readfds); FD_SET(inDescrp, &readfds); // 修正nfds:最大fd + 1 int nfds = inDescrp + 1; int nReady = select(nfds, &readfds, NULL, NULL, &timeout); // 确认目标fd确实就绪 if (nReady > 0 && FD_ISSET(inDescrp, &readfds)) { return inDescrp; } return -1; }
方案2:针对普通文件的正确检查方式
如果需要兼容普通文件,直接使用C标准库的文件操作函数更可靠:
int ioManager_nextReady(FILE *IFILE) { FILE *fp = IFILE ? IFILE : stdin; // 先检查是否已到文件末尾 if (feof(fp)) { return -1; } // 尝试读取一个字符判断可读性,成功后放回缓冲区不影响后续操作 int c = fgetc(fp); if (c == EOF) { return -1; } ungetc(c, fp); return fileno(fp); }
这种方式既适用于普通文件,也支持终端、管道等设备,逻辑更简单直接。
测试代码的小修正
你的before函数中fprintf的换行符写法存在语法问题,修正为标准的"\n":
void before(char *line) { tmpIn = tmpfile(); if (line) { fprintf(tmpIn, "%s\n", line); rewind(tmpIn); fflush(tmpIn); } }
内容的提问来源于stack exchange,提问作者Learner
相关产品推荐
相关产品推荐

