我试图了解如何在c ++或c中处理带文件的基本I / O.我的目标是逐行读取文件并将线路发送到远程服务器。如果发送了该行,我想从文件中删除它。
我试过的一种方法是保持读取行的计数并调用system()
系统调用以删除“计数”行数。我使用了bash命令:sed -i -e 1,'count'd filename
之后我继续阅读文件,令人惊讶的是它按计划工作
我有两个问题:
最佳,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的另一个问题)
答案 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)如果文件足够小:
如果您打算稍后并行化您的程序,这可能是一个更好的解决方案,只要文件很小。注意:small是一个相对术语,但通常受限于你可用的内存量。
2)如果文件非常大或受到内存限制的限制,则必须使用缓冲区来获得创意。一旦读取了一行并通过程序成功发送了该行,就可以确定文件指针的位置,并将剩余信息复制到当前文件的末尾作为新文件。完成后,关闭并删除旧文件,然后关闭并重命名与旧文件同名的新文件。
3)如果您的解决方案不必使用C ++,您可以使用shell脚本或(有争议的)另一种语言来完成工作。
答案 3 :(得分:0)
1)不,这不可靠。
2)C ++运行时库以块(内部)读取您的文件,然后将这些块分配给您的(更高级别)输入请求,直到块耗尽,迫使它(内部)读取更多从磁盘块。由于在您对sed
进行任何调用之前都会读入一个或多个物理块,因此如果sed
碰巧更改了文件的第一部分,则无法更改它们。
要查看代码失败,您需要使输入文件足够大,以便在调用sed
之前,还有未读入的文件的剩余块(在运行时库内部)。 “失败”我的意思是你的程序在sed
之前没有看到文件中最初的所有字符都被破坏了。
答案 4 :(得分:-1)
正如其他人所说,你必须在阅读原始文件后再创建一个包含所需记录的文件,然后将其删除。但是在这个应用程序中,您可能会看到更多有用的文件而不是文件。如果您在* NIX平台上,请从控制台检查makefifo语句。
它就像一个具有奇点的文件,在读取一行后会被删除。