【问题标题】:Read a file that's constantly updated (C++)读取不断更新的文件 (C++)
【发布时间】:2021-02-14 06:49:24
【问题描述】:

首先让我说我在 C++ 中大约 3 天大。

好的,主要问题,我有一个跨越多行的文件,我试图重复打印一个特定的行,这可能会被其他进程任意更改。

示例文件:

line0
line1
somevar: someval
line3
line4

我正在尝试打印中间行(以somevar 开头的行)。我的第一次天真尝试是打开文件,循环浏览内容并打印确切的行,然后移动到文件的开头。

#include <iostream>
#include <fstream>
#include <string>

int main (int argc, char *argv[])
{
    std::string file = "input.txt";
    std::ifstream io {file};

    if (!io){
        std::cerr << "Error opening file" <<std::endl;
        return EXIT_FAILURE;
    }

    std::string line;
    std::size_t pos;
    
    while (getline (io, line))
    {
        pos = line.find_first_of(' ');
        if (line.substr (0, pos) == "somevar:")
        {
            // someval is expected to be an integer
            std::cout << std::stoi( line.substr (pos) ) ) << std::endl;
            io.seekg (0, std::ios::beg);
        }
    }

    io.close();

    return EXIT_SUCCESS;
}

结果:只要文件更新,程序就会退出。

我开始认为我正在执行的 IO 实际上是缓冲的,因此更新文件不应该像那样反映在我们现有的缓冲区中(这不是 shell 脚本)。所以现在我想让我们在每次迭代时打开和关闭文件,这应该每次都有效地刷新缓冲区,我知道不是最好的解决方案,但我想测试一下理论。这是新的来源:

#include <iostream>
#include <fstream>
#include <string>

int main (int argc, char *argv[])
{
    std::string proc_file = "input.txt";
    std::ifstream io;

    if (!io){
        std::cerr << "Error opening file" <<std::endl;
        return EXIT_FAILURE;
    }
    std::string line;
    std::size_t pos;
    
    while (io.open(proc_file, std::ios::in), io)
    {
        io.sync();
        getline (io, line); 
        pos = line.find_first_of(' ');
        // The line starting with "somevar:" is always going to be there.
        if (line.substr (0, pos) == "somevar:")
        {
            std::cout << std::stoi( line.substr (pos) ) ) << std::endl;
            io.close();
        }
    }

    io.close();

    return EXIT_SUCCESS;
}

结果:和以前一样。

实现我的目标的理想方式是什么?另外,为什么每当有问题的文件被更新时程序就会退出?谢谢(:

编辑:我要读取的文件是"/proc/" + std::to_string( getpid() ) + "/io",该行是读取的字节之一(以read_bytes: 开头)。

【问题讨论】:

  • 缓冲也可能发生在写入端——即文件的实际内容(在文件系统的缓存和硬盘驱动器本身)在写入程序运行之前不能保证更新缓冲区被刷新和/或关闭其文件句柄。您是否有机会修改编写程序以使用其他机制(例如 TCP 套接字、管道或内存映射文件)?尝试使用文件作为进程间通信机制是相当不确定的。
  • 似乎是使用sqlite 的好案例。 您的问题是特定于操作系统的。如果在 Linux 上,请使用 Linux 标记您的问题。在这种情况下,请阅读 inode(7)inotify(7)
  • 请注意,文件中并不真正存在行。 somevar: someval 行可以替换为somevar: somemuchmorelongerval 吗?为什么不使用JSON 相关库?你被禁止使用现有的图书馆吗?或现有的 C++ 解析器代码生成器(例如 ANTLRGNU bison...)。我也不认为这是一个相当小的工作。如果文件很小(小于几 GB),您可以使用 mmap(2)
  • 我的感觉是你需要几个星期的全职工作。也许与提供更改文件的合作伙伴/客户讨论可能是值得的。否则,我建议直接使用 syscalls(2) 并避免使用 C++ 流。在 Linux 上,行只是与文件中的 \n(换行符)字符相关的约定!

标签: c++ linux io


【解决方案1】:

正如在 cmets 中发现的那样,您不是在读取磁盘上的“真实”文件,而是/proc/PID/io 这是一个虚拟文件,其内容只能在打开时确定,这要归功于 VFS。您说它可以“通过其他进程任意更改”的说法具有误导性,文件永远不会更改,每次打开时它的内容都不同。

所以现在我们知道,再多的寻求也无济于事。每次我们想读取文件时,我们只需要重新打开它。这可以很简单地完成:

char content[1000]; // choose a suitable value
const char key[] = "read_bytes:";
while (true)
{
    std::ifstream io(io_filename);
    if (!io.read(content, sizeof(content)))
        break;
    auto it = std::search(content, std::end(content), key, key + strlen(key));
    std::cout << atoi(it + strlen(key)) << std::endl;
}

您应该比atoi() 更小心,因为atoi() 不会在数组末尾停止,但我认为您的实际应用程序会在那里执行其他操作,因此我省略了处理。

【讨论】:

  • 虽然我确实在尝试处理/proc 的文件,但问题完全基于磁盘上的一般文件,因此标题并不完全具有误导性。使用/proc 信息是的,答案似乎更直接。谢谢(:
【解决方案2】:

我要读取的文件是一些/proc/1234/io

这是最重要的信息。

proc(5) 中的文件是小伪文件(有点像pipe(7)-s),只能按顺序读取。

那个伪文件没有更新,而是在每个open(2)处完全重新生成(由Linuxkernel,你可以研究其源代码)

因此,您只需快速读取内存中的所有文件,并在读取后处理内存中的内容。

请参阅this answer 到一个非常相关的问题.... 将其改编为 C++

【讨论】:

    猜你喜欢
    • 2013-05-19
    • 1970-01-01
    • 2014-09-30
    • 2021-02-14
    • 1970-01-01
    • 2010-10-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多