增量读取是持续监听文件末尾新增内容的动态读取方式,区别于普通读取的一次性静态快照;其实现需跟踪文件偏移、配合大小比对与合理等待,跨平台推荐二进制模式轮询,Linux 可用 inotify 事件驱动,Windows 则依赖 ReadDirectoryChangesW 目录监控。

什么是增量读取,和普通读文件有什么区别
增量读取不是一次性把整个文件读完,而是持续监听文件末尾是否有新内容追加,就像 tail -f 那样——程序不退出,只在有新增行时才触发处理。关键在于:不能反复从头读、不能阻塞太久、不能漏掉中间写入的字节。
普通 fopen + fread 或 std::ifstream 默认是静态快照式读取,不感知后续追加;而增量读取必须配合文件偏移跟踪 + 重复检查 + 合理等待机制。
用 std::ifstream + tellg() / seekg() 实现基础轮询
最轻量、跨平台的做法是靠记录上次读到的位置,每次循环中检查文件大小是否增长,再从该位置继续读。注意:必须用二进制模式打开,避免文本模式下换行符转换干扰偏移计算。
-
std::ifstream要用std::ios::binary标志打开,否则tellg()在 Windows 上可能返回错误偏移 - 每次读前先调用
file.seekg(0, std::ios::end)获取当前文件大小,与上次记录的pos比较 - 如果大小增加,就
seekg(pos),然后用getline()或read()读新增部分 - 读完更新
pos = file.tellg()(注意:若读到 EOF,tellg()可能返回 -1,需用file.seekg(0, std::ios::end)再确认)
示例关键片段:
立即学习“C++免费学习笔记(深入)”;
std::ifstream file("log.txt", std::ios::binary);
file.seekg(0, std::ios::end);
std::streampos pos = file.tellg();
while (running) {
file.seekg(0, std::ios::end);
std::streampos newSize = file.tellg();
if (newSize > pos) {
file.seekg(pos);
std::string line;
while (std::getline(file, line)) {
process(line);
}
pos = file.tellg(); // 注意:getline 到 EOF 后 tellg() 不可靠,保险起见可设为 newSize
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
Linux 下用 inotify 监听文件变化更高效
轮询有延迟且浪费 CPU;Linux 提供 inotify 系统调用,能真正事件驱动地获知文件是否被追加(IN_MODIFY),但要注意:IN_MODIFY 触发频繁(每写一次 buffer 就可能触发),且不保证写入已落盘,仍需配合偏移检查。
- 调用
inotify_init1(IN_NONBLOCK)创建句柄,再用inotify_add_watch(fd, path.c_str(), IN_MODIFY) - 用
read()非阻塞读取事件缓冲区,解析struct inotify_event,过滤出目标文件事件 - 收到事件后,仍要像轮询方式一样检查文件大小并读新增内容——
inotify只告诉“变了”,不告诉“变了多少” - 务必处理
EAGAIN错误,因为设置了IN_NONBLOCK;也可用poll()等待事件,避免忙等
比轮询省资源,但仅限 Linux,且需要手动管理 fd 和事件缓冲区大小(通常 4KB 足够)。
Windows 下用 ReadDirectoryChangesW 替代 inotify
Windows 没有 inotify,对应方案是 ReadDirectoryChangesW,但它监听的是目录级变更,且对单个文件的“追加”事件不直接支持——它会报告 FILE_NOTIFY_CHANGE_LAST_WRITE,但同样无法区分是修改还是追加,仍需结合文件大小判断。
- 必须以
FILE_FLAG_BACKUP_SEMANTICS打开目录句柄,而非文件句柄 - 传入的缓冲区需足够大(建议至少 4096 字节),事件结构体是变长的,需按
NextEntryOffset遍历 - 事件类型为
FILE_ACTION_MODIFIED时,再打开目标文件查大小、读新增内容 - 该 API 是异步的,推荐搭配
OVERLAPPED+ I/O 完成端口,否则容易丢事件
相比 Linux 的 inotify,Windows 这套更重、更易出错,尤其要注意缓冲区溢出和事件丢失问题——如果写入频率高,没及时读完缓冲区,后续事件会被丢弃。
实际项目中,若只跑 Linux,优先用 inotify;若需跨平台,轮询仍是更稳妥的选择,只要 sleep 时间设合理(如 100–500ms),CPU 开销并不高。真正的难点不在“怎么读”,而在“如何准确识别哪段是新内容”——特别是当文件被截断(truncate)或重写时,旧偏移失效,需要额外逻辑检测并重置位置。


















