从文件中删除第一行

时间:2012-07-19 18:52:09

标签: c# c++ performance file io

  

可能重复:
  Removing the first line of a text file in C#

从一个巨大的(想想2-3 GB)文件中删除第一行的最快最聪明的方法是什么?

  • 我认为,你可能无法避免重写整个文件,但我可能错了。

  • 使用内存映射文件能否以某种方式帮助解决此问题?

  • 是否可以通过直接在文件系统上运行(例如NTFS)来实现此行为 - 例如,更新相应的inode数据并更改文件的起始扇区,以便第一行被忽略了?如果是的话,这种方法是否真的很脆弱,或者有许多其他应用程序,除了OS本身做类似的东西?

5 个答案:

答案 0 :(得分:13)

默认情况下,

NTFS在大多数卷上(但重要的是不是全部!)将数据存储在4096个字节块中。这些由$MFT记录引用,您无法直接编辑,因为操作系统不允许这样做(出于理智的原因)。因此,没有任何技巧可用于操作文件系统以执行接近您想要的操作(换句话说,您无法直接反转截断NTFS上的文件,即使是文件系统块大小的数量。)

由于文件存储在文件系统中的方式,唯一的答案是您必须直接重写整个文件。或者找出一种不同的方式来存储您的数据。一个2-3GB的文件是庞大而疯狂的,特别是考虑到你提到的行意味着这些数据至少部分是文本信息。

您应该考虑将这些数据放入数据库吗?或者至少可以更有效地组织它。

答案 1 :(得分:8)

您可以使用'\x7f'覆盖要删除的每个字符。然后,当读入文件时,您的读者会忽略该字符。这假设您有一个文本文件,当然不会使用DEL字符。

std::istream &
my_getline (std::istream &in, std::string &s,
            char del = '\x7f', char delim = '\n') {
    std::getline(in, s, delim);
    std::size_t beg = s.find(del);
    while (beg != s.npos) {
        std::size_t end = s.find_first_not_of(del, beg+1);
        s.erase(beg, end-beg);
        beg = s.find(del, beg+1);
    }
    return in;
}

正如Henk指出的那样,你可以选择一个不同的角色作为你的DELETE。但是,优点是无论您要删除哪一行(不仅限于第一行),该技术都可以工作,并且不需要使用文件系统。

使用修改过的阅读器,您可以定期对文件进行“碎片整理”。或者,碎片整理可能会自然发生,因为内容被流式传输/合并到不同的文件中或存档到不同的机器。

编辑:您没有明确说出来,但我猜这是针对某种日志记录应用程序,其目标是在日志文件的大小上加上一个上限。但是,如果这是目标,则使用较小的日志文件集合要容易得多。假设您保留了大约10MB的日志文件,总日志限制为4GB。那将是大约400个文件。如果启动了第401个文件,则对于每个写入的行,您可以在第一个文件中的连续行上使用DELETE标记。当所有行都被标记为删除时,可以删除文件本身,再次为您留下大约400个文件。只要在删除行时第一个文件没有关闭,就没有隐藏的O(n 2 )行为。

但更简单的是允许您的日志记录系统保留第1个和第401个文件,并在移动到第402个文件时删除第1个文件。

答案 2 :(得分:6)

即使你可以移除一个前导块,它至少会是一个扇区(512字节),可能与你的线路大小不匹配。

考虑一个包装器(甚至可能是辅助文件)来开始从某个偏移量开始读取。

答案 3 :(得分:3)

理念(没有魔力,只有下面的努力工作):

使用用户模式文件系统(例如http://www.eldos.com/cbfs/http://dokan-dev.net/en/)围绕真实文件系统进行WRAP,并创建一个小型簿记系统来跟踪前面“吃掉”的文件数量。在某些时候,当文件变得太大时,将文件重写为另一个并重新开始。

那怎么样?

编辑:

如果你使用虚拟文件系统,那么你可以使用较小的(256mb)文件片段,你可以将它们粘合到一个具有所需偏移量的“虚拟”文件中。这样你就不需要重写文件了。

MORE:

关于'覆盖'前几行'没有'的想法的反思 - 不要这样做,而是在文件的FRONT中添加一个64位整数,并使用你喜欢的任何方法跳过那么多bytes,例如Stream派生,它将包装原始流并抵消其中的读数。

如果您选择在“客户”端使用包装器,我想这可能会更好。

答案 4 :(得分:0)

将文件分成两部分,第一部分是较小的部分。 删除第一行,然后与另一行连接。