C ++在特定行号的文件中插入一行

Posted

技术标签:

【中文标题】C ++在特定行号的文件中插入一行【英文标题】:C++ inserting a line into a file at a specific line number 【发布时间】:2008-11-20 16:05:34 【问题描述】:

我希望能够读取未排序的源文本文件(每行一条记录),并通过指定应插入的行号将行/记录插入到目标文本文件中。

将行/记录插入目标文件的位置将通过将传入文件中的传入行与目标文件中已排序的列表进行比较来确定。 (目标文件将作为一个空文件开始,当程序遍历传入的文件行时,数据将被排序并一次插入一行。)

传入文件示例:

1 10/01/2008 line1data
2 11/01/2008 line2data
3 10/15/2008 line3data

所需的目标文件示例:

2 11/01/2008 line2data
3 10/15/2008 line3data
1 10/01/2008 line1data

我可以通过链表或类似方法在内存中执行排序来做到这一点,但我想让它扩展到非常大的文件。 (而且我很高兴尝试解决这个问题,因为我是 C++ 新手 :)。)

其中一种方法可能是使用 fstream 打开 2 个文件流(1 输入和 1 输出,或仅 1 输入/输出流),但后来我遇到了很难找到和搜索文件位置,因为它似乎取决于文件开头的绝对位置而不是行号:)。

我确信这样的问题以前已经解决过,如果以良好做法的方式进行操作,我将不胜感激。

我正在使用 Visual Studio 2008 Pro C++,我只是在学习 C++。

【问题讨论】:

【参考方案1】:

基本问题是在普通操作系统下,文件只是字节流。在文件系统级别没有行的概念。这些语义必须作为附加层添加到操作系统提供的设施之上。虽然我从未使用过它,但我相信 VMS 有一个面向记录的文件系统,它可以让你想做的事情变得更容易。但是在 Linux 或 Windows 下,你不能在不重写文件的其余部分的情况下插入文件的中间。它类似于内存:在***别,它只是一个字节序列,如果您想要更复杂的东西,例如链表,则必须将其添加到顶部。

【讨论】:

【参考方案2】:

如果文件只是一个纯文本文件,那么找到特定编号行的唯一方法恐怕就是边走边计算文件行数。

通常的“非内存”方法是将文件从原始文件复制到临时文件,在正确的位置插入数据,然后重命名/替换原始文件。

显然,一旦完成插入,您就可以将文件的其余部分复制到一个大块中,因为您不再关心行数。

【讨论】:

【参考方案3】:

[distinctly-no-c++] 解决方案是使用 *nix sort 工具,对第二列数据进行排序。它可能看起来像这样:

cat <file> | sort -k 2,2 > <file2> ; mv <file2> <file>

它并不完全就地,它没有使用 C++ 的请求,但它确实有效:)

甚至可以做到:

cat <file> | sort -k 2,2 > <file>

不过,我还没有尝试过这条路线。 * http://www.ss64.com/bash/sort.html - 排序手册页

【讨论】:

是的,这是显而易见的解决方案,但我不确定sort 的实现是否可以扩展到提到的巨大文件。 真的...只是想提供一个替代方案:) @Svante:我对超过 5 GB 的文件使用了 GNU 排序,效果很好。 谢谢,赞。 @warren:对了,这里用cat是多余的,sort可以把文件当做参数。 (这种使用cat 是一种反模式。) @Svante - 我知道sort 可以接受文件命令,但对我来说将命令拆分为每个部分是有意义的(显示文件,对其进行排序,对其进行处理) :)【参考方案4】:

这样做的一种方法不是保持文件排序,而是使用单独的索引,使用 berkley db (BerkleyDB)。数据库中的每条记录都有排序键和主文件的偏移量。这样做的好处是您可以有多种排序方式,而无需复制文本文件。您还可以通过在末尾附加更改的行并更新索引以忽略旧行并指向新行来更改行而不重写文件。我们成功地将其用于需要对其进行许多小改动的多 GB 文本文件。

编辑:我为此开发的代码是一个更大的包的一部分,可以下载here。具体代码在source/IO下的btree*文件中。

【讨论】:

【参考方案5】:

尝试修改后的Bucket Sort。假设 id 值很适合它,您将获得更有效的排序算法。您可以通过在扫描时实际写出存储桶(使用小存储桶)来提高 I/O 效率,从而可能减少您需要的随机文件/io 的数量。或者不。

【讨论】:

【参考方案6】:

希望有一些很好的代码示例说明如何将基于行号的记录插入到目标文件中。

您不能将内容插入文件的中间(即,不覆盖之前的内容);我不知道支持它的生产级文件系统。

【讨论】:

我不知道。我只是假设 C++ 支持这一点,因为其他语言也支持(最近想到的是 mirc 脚本)【参考方案7】:

我认为问题更多是关于实现而不是具体算法,特别是处理非常大的数据集。

假设源文件有 2^32 行数据。什么是对数据进行排序的有效方法。

我会这样做:

    解析源文件并提取以下信息:排序键、文件中行的偏移量、行的长度。此信息被写入另一个文件。这会生成一个易于索引的固定大小元素的数据集,称为索引文件。

    使用修改后的合并排序。递归划分索引文件,直到要排序的元素数量达到某个最小值 - 真正的合并排序递归到 1 或 0 个元素,我建议停在 1024 或其他地方,这需要微调。将索引文件中的数据块加载到内存中并对其执行快速排序,然后将数据写回磁盘。

    对索引文件执行合并。这很棘手,但可以这样做:从每个源加载一个数据块(例如 1024 个条目)。合并到一个临时输出文件并写入。当一个块被清空时,重新填充它。当找不到更多源数据时,从头开始读取临时文件并覆盖正在合并的两个部分 - 它们应该是相邻的。显然,最终的合并不需要复制数据(甚至创建一个临时文件)。考虑到这一步,可能可以为合并的索引文件设置一个命名约定,这样数据就不需要覆盖未合并的数据(如果你明白我的意思的话)。

    读取排序后的索引文件,从源文件中取出该行数据写入结果文件。

读取和写入所有文件肯定不会很快,但应该非常高效 - 真正的杀手是在最后一步中随机查找源文件。到目前为止,磁盘访问通常是线性的,因此应该相当有效。

【讨论】:

您是否按照最初的解决方案:cm.bell-labs.com/cm/cs/pearls/sec013.html? 有点(我猜是上图)。 'wonder sort' 位图在这里不起作用,因为 a) 有额外的数据 b) 可能有重复的键。

以上是关于C ++在特定行号的文件中插入一行的主要内容,如果未能解决你的问题,请参考以下文章

sed如何在文件的最后 插入一行文字

使用 sed 或 awk 在特定行号处插入一行

使用 sed 或 awk 在特定行号处插入一行

C ++在文本文件中的特定字符后插入新行

LINUX C语言,在文本中某一行插入内容,最好有程序。

插入文件内容作为文件的第一行;在 bash (GNU) 中