【问题标题】:Remove first line from a file [duplicate]从文件中删除第一行[重复]
【发布时间】:2012-07-19 00:36:39
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 更改第一行后,您将重写剩余的内容。考虑对文件进行反向排序...
  • 您应该用相关的操作系统和 FS 标记您的问题 - 可能存在一个文件系统/操作系统组合,可以编辑磁盘上的文件。
  • 没有什么特别的技巧。 NTFS 不能那样工作。
  • 用例会有所帮助。你控制文件的生成吗?解析器?还有其他变量吗?
  • 你想到了什么样的文件??

标签: c# c++ performance file io


【解决方案1】:

将文件一分为二,第一个是较小的块。 删除第一行,然后附加另一行。

【讨论】:

    【解决方案2】:

    您可以使用'\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(n2) 行为。

    但更简单的方法是让您的日志记录系统保持第 1 和第 401 文件不变,并在移动到第 402 文件时删除第 1 文件。

    【讨论】:

    • 是的,聪明的主意。或者用空格、换行符或\0s 覆盖。但这一切都取决于读者,以及它可以适应多少。
    • @HenkHolterman:你是对的。我更新了帖子以反映可以选择不同的角色。问候
    【解决方案3】:

    想法(没有魔法尘埃,只有下面的努力):

    使用http://www.eldos.com/cbfs/http://dokan-dev.net/en/ 等用户模式文件系统来包裹您的真实文件系统,并创建一个小型簿记系统来跟踪有多少文件在前面被“吃掉”。在某些时候,当文件变得太大时,将文件重写为另一个文件并重新开始。

    怎么样?

    编辑:

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

    更多:

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

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

    【讨论】:

      【解决方案4】:

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

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

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

      【解决方案5】:

      即使您可以删除前导块,它也至少是一个扇区(512 字节),可能与行的大小不匹配。

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

      【讨论】:

        猜你喜欢
        • 2014-06-30
        • 2012-06-01
        • 2018-05-23
        • 2019-07-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多