从文件中删除第一行

21

可能是重复问题:
在C#中删除文本文件的第一行

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

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

  • 使用内存映射文件是否有助于解决此问题?

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


更改第一行后,您将不得不重新编写其余内容。考虑以相反的顺序对文件进行排序... - Oded
2
你应该在问题中标记操作系统和文件系统 - 可能有一种文件系统/操作系统组合具有编辑磁盘上文件的能力。 - Daniel Mošmondor
没有什么特别的技巧。NTFS不是那样工作的。 - OmnipotentEntity
3
使用案例可以帮助理解。您控制文件的生成吗?解析器?还有其他变量吗? - scorpiodawg
1
你想要什么类型的文件? - Mitja Bonca
以下是其中一种解决方案:https://dev59.com/cHRB5IYBdhLWcg3wSVi1 - Mitja Bonca
5个回答

13
默认情况下,大多数(但重要的是不是全部)NTFS 卷将数据存储在 4096 字节的块中。这些由 $ MFT 记录引用,您无法直接编辑该记录,因为操作系统禁止这样做(出于理智的原因)。因此,在文件系统上操作以实现所需效果的方法并不存在(换句话说,在 NTFS 上不能直接按文件系统块大小逆向截断文件)。
由于文件存储在文件系统中的方式,唯一的解决办法是必须直接重写整个文件或者想出一种不同的方式来存储数据。一个2-3GB的文件非常庞大和疯狂,特别是考虑到您提到行,这意味着这些数据至少部分是文本信息。
您应该考虑将这些数据放入数据库中,也许?或者至少更有效地组织它。

1
你可以使用稀疏文件。 - Joey

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²)行为。
但更简单的方法是允许您的日志系统保留第一个和第401个文件,并在移动到第402个文件时删除第一个文件。

1
是的,聪明的想法。或者用空格、换行符或\0覆盖。不过这完全取决于读者以及它能够被适应到多大程度。 - H H
@HenkHolterman:你说得对。我更新了帖子,以反映可以选择不同的字符。问候 - jxh

6
即使您可以删除一个前导块,它至少也是一个扇区(512字节),可能与您的行大小不匹配。考虑使用包装器(甚至是辅助文件)来从特定偏移量开始读取。

3

Idea(没有魔法,只有下面的辛勤工作):

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

这个想法怎么样?

编辑:

如果你选择虚拟文件系统,那么你可以使用更小的(256mb)文件片段,然后将它们粘合成一个带有所需偏移量的“虚拟”文件。这样,你就永远不需要重新编写文件。

更多:

关于“覆盖”前几行的想法的反思 - 不要那样做,相反,在文件的前面添加一个64位整数,并使用任何你喜欢的方法跳过那么多字节,例如Stream派生,它将包装原始流并在其中进行偏移读取。

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


0

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接