使用system()在C ++程序中读取行后删除行

时间:2014-01-11 05:10:38

标签: c++ bash sed

我试图了解如何在c ++或c中处理带文件的基本I / O.我的目标是逐行读取文件并将线路发送到远程服务器。如果发送了该行,我想从文件中删除它。
我试过的一种方法是保持读取行的计数并调用system()系统调用以删除“计数”行数。我使用了bash命令:sed -i -e 1,'count'd filename 之后我继续阅读文件,令人惊讶的是它按计划工作 我有两个问题:

  1. 这样可靠吗?
  2. 为什么这会起作用呢? 阅读文件我删除了它的一部分,但它的工作原理?如果我 是什么寻求到以前的位置,那么呢?
  3. 最佳,Digvijay

    PS:
    如果有人能提出更好的方法,我会很高兴。

    此处还有我编写的程序代码:

    #include<iostream>
    #include<fstream>
    #include<string>
    #include<sstream>
    #include<cstdlib>
    
    int main(){
        std::ifstream f;
        std::string line;
        std::stringstream ss;
        int i=0;
        f.open("in.txt");
        if(f.is_open()){
            while(getline(f,line)){
                std::cout<<line<<std::endl;
                i++;
                if(i==2)break;
            }
            ss<<"sed -i -e 1,"<<i<<"d in.txt";
            system(ss.str().c_str());
            while(getline(f,line)){
                std::cout<<line<<std::endl;
            }
        }
        return 0;
    }
    

    修改 首先感谢花时间写答案。但是这里有一些我之前错过的额外信息。
    我正在处理的文件是日志文件。因此,他们不断被添加来自设备的信息。我想避免创建副本的原因是,因为日志文件本身非常大(有时),加上这将有助于保持日志文件的简短。因为它们将被分成几部分并存档在服务器上 的解决方案 我找到了解决问题的方法。显然托马斯是对的,sed确实创建了一个新文件。所以旧文件保持原样。使用它,我可以读取n行,调用系统函数,关闭文件指针并再次打开它。我在日志的小块上执行此操作,反复进行,直到它变小并因此有效处理。服务器将日志归档到1gb文件中。 但是我有一个新问题,由于内存限制,我需要知道是否可以有效地将日志文件拆分为两个。 (这可能是关于SO的另一个问题)

5 个答案:

答案 0 :(得分:2)

大多数现代文件系统不支持在文件开头删除行,因此这样做效率很低。

实际问题的正常解决方案是在达到某个大小时停止写入日志文件,然后开始写入新文件。复制文件的代码可以在写入文件后删除整个文件(这是一种有效的操作)。

答案 1 :(得分:1)

sed写了一个新版本的文件,而程序继续读取它打开的相同版本。当程序写入另一个程序打开的文件时,这是Unix和Linux的常见行为。

你可以通过这个小C程序自己看到这个:

#include <stdlib.h>
#include <stdio.h>

int main(void) {
    FILE *f = fopen("in.txt", "r");
    while (1) {
        rewind(f);
        int lines = 0;
        int c;
        while ((c = getc(f)) != EOF)
            if (c == '\n')
                ++lines;
        printf("Number of lines in file: %d\n", lines);
    }
    return 0;
}

在一个窗口中运行该程序,然后在另一个窗口中使用sed编辑该文件。即使磁盘上的文件已被编辑,程序打印的行数也将保持不变,这是因为Unix保留旧的开放版本,即使其他程序不再可以访问它。

至于你的第一个问题,你的解决方案有多可靠,据我所知,它应该是可靠的,除了在更新过程中系统崩溃或内存耗尽的常见警告,其他人访问该文件,当然还有系统调用的所有问题。但是,它效率不高,对于大型数据集,您可能希望采用不同的方式。

sujin关于为你想要保留的行使用临时文件的评论似乎是合理的。它会更快更安全。保留原始文件,因此如果系统崩溃,您仍然会有数据,并等到完成将旧文件重命名为“in.txt.bak”,然后将临时文件重命名为“in.txt”

答案 2 :(得分:0)

首先,尽可能避免使用system次呼叫(如果可能的话,根本不要使用它),因为它们会极大地(通常){{3}创建竞争条件和其他问题}。如果涉及对文件的访问,则尤其如此。

考虑到你的问题,有很多方法可以做到这一点,每个方法都有自己的注意事项。

我将介绍三种可能的解决方案:

1)如果文件足够小:

  1. 你可以在数据结构(矢量,列表,双端队列等)中读取整个内容。
  2. 删除原始文件
  3. 确定要读取的行数(并通过服务器协议发送)
  4. 然后将剩余的行写为原始文件的名称。
  5. 如果您打算稍后并行化您的程序,这可能是一个更好的解决方案,只要文件很小。注意:small是一个相对术语,但通常受限于你可用的内存量。

    2)如果文件非常大或受到内存限制的限制,则必须使用缓冲区来获得创意。一旦读取了一行并通过程序成功发送了该行,就可以确定文件指针的位置,并将剩余信息复制到当前文件的末尾作为新文件。完成后,关闭并删除旧文件,然后关闭并重命名与旧文件同名的新文件。

    3)如果您的解决方案不必使用C ++,您可以使用shell脚本或(有争议的)另一种语言来完成工作。

答案 3 :(得分:0)

1)不,这不可靠。

2)C ++运行时库以块(内部)读取您的文件,然后将这些块分配给您的(更高级别)输入请求,直到块耗尽,迫使它(内部)读取更多从磁盘块。由于在您对sed进行任何调用之前都会读入一个或多个物理块,因此如果sed碰巧更改了文件的第一部分,则无法更改它们。

要查看代码失败,您需要使输入文件足够大,以便在调用sed之前,还有未读入的文件的剩余块(在运行时库内部)。 “失败”我的意思是你的程序在sed之前没有看到文件中最初的所有字符都被破坏了。

答案 4 :(得分:-1)

正如其他人所说,你必须在阅读原始文件后再创建一个包含所需记录的文件,然后将其删除。但是在这个应用程序中,您可能会看到更多有用的文件而不是文件。如果您在* NIX平台上,请从控制台检查makefifo语句。

它就像一个具有奇点的文件,在读取一行后会被删除。