【问题标题】:Reordering of data segments within a file文件中数据段的重新排序
【发布时间】:2012-01-17 00:21:23
【问题描述】:

我正在尝试找到一种算法,其中可能包含 C、C++、C#、Java 或任何语言的示例,以帮助解决我一直面临的重新排序问题。

目标是在一个文件中获取一系列范围,并以新的模式重新组织,基本上是在不破坏数据完整性的情况下移动数据片段。我希望找到一个可以就地执行它并使用单个缓冲区进行交换或直接从一个地方移动到另一个地方的缓冲区。只要范围在完成时具有相同的长度和数据完整性,重组过程就可以将范围分解为多个部分。

举个例子,给定一组值:

  Length    SrcStart     Src End   Dst Start     Dst End
    9178      274054      283231           0        9177
  274051           0      274050        9178      283228
  582929      283229      866157      283229      866157
  399208      874397     1273604      866158     1265365
    8239    14675709    14683947     1265366     1273604
  986980     1273605     2260584     1273605     2260584
  602862     2811144     3414005     2260585     2863446
  138712     4092072     4230783     2863447     3002158
  116210     3414007     3530216     3002159     3118368
  550559     2260585     2811143     3118369     3668927
  561856     3530217     4092072     3668928     4230783
24319165     4230784    28549948     4230784    28549948
  578539    30246149    30824687    28549949    29128487
  491856    28549949    29041804    29128488    29620343
  593580    29639113    30232692    29620344    30213923
  597308    29041805    29639112    30213924    30811231
   13456    30232693    30246148    30811232    30824687
  633513    31407949    32041461    30824688    31458200
  583261    30824688    31407948    31458201    32041461
40117358    32041462    72158819    32041462    72158819

需要将 SrcStart -> SrcEnd 范围内的所有内容移至 DstStart -> DstEnd 范围。请注意,在许多情况下,从 Source 转移到 Destination 会导致 Destination 的内容被更改,由于所需的原始数据已被破坏,您无法再从该位置复制这些内容。

目标是将每个数据段从 SrcStart 移动到 DstStart,其中 Length 在第一列。每行对应的“结束”只是开始加上长度减一(因此它是实际的偏移量)。

我做了很多研究,研究了交换值,并分解了与其他值交叉的区域以及容器交换中的容器,但它们似乎不足。因此,结果,这让我回到了我的第一个陈述,我希望也许有一种算法或一些资源可以帮助我解决这个问题,而社区的共享知识似乎就是这样去。

谢谢!

【问题讨论】:

  • 您是否考虑过将值读入 Dictionary 或 HashTable 或 List 或 SortedList 如果您从文件中读取,您应该能够快速加载数据从文件到一个集合或多个列表或多个哈希表,并在该列表上使用添加、删除、排序方法。我假设数据的布局就像文件中的那样..?如果情况更糟,您始终可以使用字段名称创建一个枚举并使用枚举的 (int) 值,该值将表示枚举中声明的字段名称作为标题布局。
  • 这个移动动作列表实际上是如何计算的?你在整理碎片吗?然后查看此链接:forums.devshed.com/c-programming-42/…
  • 我认为你可以跳过最后一行,因为源和目标是相同的:40117358 32041462 72158819 32041462 72158819 哦,还有第 3 行、第 6 行和第 12 行。
  • @DJKRAZE John 正在尝试重新排列表中行指定位置处的数据,他没有尝试重新排列表内容。也许他应该说得更清楚一点。
  • 我同意.. 感谢您的澄清,很难从他最初的帖子中收集到这一点。再次感谢大家周末愉快

标签: c# java c++ language-agnostic


【解决方案1】:

您可以使用磁盘碎片整理程序使用的方法。

  • 先将需要覆盖的数据复制到空闲区域
  • 将引用该数据的所有索引更改为指向新位置,以便将来使用该副本。
  • 如果系统有这样的概念,您可能需要注意是否有任何块“未使用”。

但是,如果索引以字节为单位,则意味着整个文件只有 80 MB。一个很小的文件可以非常快速地复制(花费不到两秒钟)也许真正的例子要长得多。文件总共有多大?

【讨论】:

  • 该文件确实是 80 兆,但还有其他的文件可以有 2GB 的大小,当然你永远不想在非服务器环境中加载到内存中。也就是说,这个文件的问题是确实没有任何可用空间可供使用。它的所有纯粹的转变和使用另一个文件作为“垃圾场”只会使执行任务所需的空间增加一倍,从而挫败就地的想法。
  • 你需要有一些空间来复制数据,即使那是内存中的一部分。我的电脑有 24 GB,它不完全是服务器。 ;)
  • 这是针对拥有 2GB-4GB 内存的普通消费类 PC。我不介意使用 8-16 megs 的内存来交换东西并考虑它,因为我可以在 16 meg 突发中执行多个读/写模式也应该很快。
  • 在内存中执行此操作的问题是,如果进程在任何时候中断,文件将被损坏。但是,您可以重写部分。它不会比复制文件并在完成后删除原始文件更快,但风险更大。
  • 感谢您的回复。有一个自我纠正的审计系统可以修复损坏的对象,所以我不担心。我了解所有可能发生的外围事情,但请理解这里的目标仍然是移动数据,或者找到可能有助于帮助实现这一目标的算法或代码。
【解决方案2】:

您提出的问题好像它是一个不透明的二进制文件,出于某种原因您想在其中交换块。情况似乎不太可能真的如此。文件肯定有自己的结构吗?你不能利用它来帮助你记账吗?

  • 文件是否有“已使用”和“未使用”区域的概念?
  • 文件是否具有块头的内部结构?
  • 文件是否有任何关联的索引或任何需要保持同步的内容? (如果没有,您从哪里获得要移动的方块列表?)
  • 移动的方块可以互相重叠吗?请注意,如果可以,操作顺序就会变得很重要。

也就是说,@Peter Lawrey 推荐的方法很好。如果覆盖一个块,首先将它复制到文件中的另一个位置,更新所有重叠的索引。

在我看来,您似乎试图通过将一个难题分解为两个步骤来解决一个难题,一个简单,另一个……甚至更难。最初的问题是什么?

(强制性建议:在 Windows 上可能使用事务性 IO API)。

【讨论】:

  • 没有“标准”格式,可以是任何二进制格式。举个例子,假设您有一个 zip 文件,然后解压缩它,然后使用所有名称,然后将其重新压缩。您将几乎没有更改数据(索引),但大部分数据将保持不变,只是在文件中移动,在您开始提升对象之前不会给您真正的任何大“洞”。您可以通过取出所有“找到的”对象并按大小对它们进行排序来解决耗尽孔空间的问题,这样您就可以将较大的对象取出并按顺序替换较小的对象,但这并不能解决整体问题。
【解决方案3】:

我认为以下算法最多可以使用最大卡盘大小的两倍来缓存数据。除了数据缓存之外,您还需要一个簿记 FIFO 和原始列表。它是这样的:

  1. 如果 FIFO 和移动表都为空,则结束。
  2. 如果 FIFO 为空,则将移动表中的顶部条目移动到 FIFO,同时将条目数据读出到数据缓存中。
  3. 检查是否有任何块与移动表中 FIFO 中第一个条目的目标区域重叠。
  4. 如果有block,将block的数据读入cache,将entry移到FIFO,转3。
  5. 如果没有block,将FIFO entry的数据从cache写入destination,移除第一个FIFO entry,执行1。

这个想法是找到被占用的块,缓存它们以打开空间,并尽快摆脱缓存中的数据。您可能需要添加一些完整性检查以查看源地址和目标地址是否相同。您可能还需要进行额外检查以查看原始表格是否有意义(两个块移动到同一位置等)。


编辑:我可能一直乐观地陈述最大区块乘以两次估计。我认为它可以超越这一点。我认为时间 3 是一个简单(但宽松)的上限。

由于您的源表中有相当大的块,您可以将它们拆分以减少缓存使用。假设您想使用最多 1 GB 的缓存:在运行算法之前将所有大于 1/3GB 的块拆分为多个 1/3GB 长度的条目。或者,您可以使算法以子块大小工作(而不是将整个块读入缓存,您只读取相关部分,并将修改后的条目保留在源表中),但我认为这将更难以管理/实施。

【讨论】:

  • 我会考虑一下,我可以看到的问题是,一旦开始(前进或后退),并且每次越过 2 个或更多元素的边界,您就必须完成链流(想想部分: 30246149-30824687 28549949-29128487 跨越 28549949-29041804 29128488-29620343 和 29041805-29639112 30213924- 30811231,但是你必须在运动前/后运动部分,然后是简单的转变检查以查看其他对象边界(因为您现在通过拆分有 2 个移动元素对象,您需要在后一个移动对象上进行拆分)以便它增长。
  • 所以 30246149-30824687 移动到 28549949-29128487 意味着我需要后者先进入 fifo,因为我们即将覆盖它。但是,我需要将数据拆分并准备好不仅用于 28549949-29041804(转到 29128488-29620343),还需要拆分为 29041805-29128487 / 29128488-29639112 和 30213924-303006306 / 303310612如果我遇到一个它不断分裂的事件,我最终会超出我的缓冲区大小,因为我正在补偿多个部分而不是 1:1 部分到点甚至 1:2。也许可以用你的方法向我展示最后 8 行的“逐步解决”?
  • 首先,在没有拆分块的情况下,上述算法的界限比我预测的要高得多。考虑打包在一起的大小相等的块(由 1、2、3、4、...标识),这些块的目的地为 1->(2,3)、2->(4,5)、3-> (6,7), 4->(8,9)...;其中 (i,j) 表示跨越 i 和 j(因为所有块的大小相同,您可以说偏移量是块 i 大小的一半)。上述设置将导致 FIFO 不断增长:2,3; 3、4、5; 4、5、6、7; 5, 6, 7, 8, 9 ...因此,这样的设置将使您尝试移动的每个块将 FIFO 增加一个块(您弹出一个,但再推两个)。
  • 此时,处理此问题的最佳方法是修改算法,仅将目标的重叠部分移出到 FIFO。这样,FIFO 的大小将始终受空时推送的块大小的两倍(我认为)。 FIFO的顶部在撤离点之前只能带入小于或等于其自身大小的数据,因此FIFO的总大小逐渐下降,直到清空为止。但是该算法的这种变体具有更高的复杂性。
  • 你的第二点也是有效的。如果移位块的一部分在 FIFO 中,您最终会拆分移位块,直到完成整个移位。如果您在移动 FIFO 内的块后丢弃它们,这只会导致效率低下(将部分拆分为 FIFO,将 FIFO 拆分为空白空间,重复直到整个块被移动),以及 1 字节拆分的退化情况。如果您保留家政数据,那么您就有麻烦了。完成后,您还需要合并拆分部分。您可能希望检测移位并将其作为特殊情况处理。
猜你喜欢
  • 2021-12-25
  • 2020-11-23
  • 1970-01-01
  • 2021-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多