0

我需要一个库,它可以帮助我以压缩格式(本质上是迷你 DSL)保存和查询数据,这是我想要的示例:

更新 1 - 请注意,上面示例中的数字被缩小只是为了更容易遵循逻辑,实际数字受c# long类型容量的限制,例如: 1,18,28,29,39,18456789,18456790,18456792,184567896.

样本原始数据集:1,2,3,8,11,12,13,14

压缩样本数据集: 1..3,8,11..14

能够1,2,4,5,6,7,8,9,101..10-3.

查询样本数据集:

查询 1(获取范围): 1..5->1..3

查询2(检查值是否存在) ?2->true

查询 3(获取多个范围和标量值): 1..5,11..12,14->1..3,11..12,14

我不想从头开始开发它,并且非常喜欢使用已经存在的东西。

4

2 回答 2

1

我不知道有任何现成的库可以完全满足您的需求,但我不确定您是否需要一个。

我建议你考虑使用现有的BitArray类。如果,正如您的示例所暗示的,您对压缩小整数集感兴趣,那么单个BitArray具有(例如 256 位)的整数可以表示 range 中的任何整数集[0..255]。当然,如果您的典型集合中只有 5 个整数,那么这种方法实际上会扩展您的存储需求;您必须根据自己对集合的了解来确定此类数组的正确大小。

我建议还将您的数据视为整数集,因此您的示例1,2,3,8,11,12,13,14将通过设置 a 中的相应位来表示BitArray。然后,您的查询操作减少为测试BitArray和数据之间的交集BitArray

顺便说一句,我认为您的示例 22 -> true会更好地停留在将整数集映射到整数集的函数域中,即它应该 transform 2 -> 2。如果您愿意,请编写一个返回布尔值的不同方法。

我猜你需要编写代码来将整数打包和解BitArraysBitArrays成整数,但这是压缩成本的一部分。

于 2012-09-26T08:57:45.013 回答
1

自从我阅读您的问题以来,这是我这些天以来的一些想法。我不能确定它们中的任何一个是否真的适用于您的用例,但我希望您能在这里找到有用的东西。

存储压缩数据

您可以采取的步骤来减少数字在磁盘上占用的空间量:

  • 如果您的值在 1 到 ~10M 之间,请不要使用 a long,使用 a uint。(每个数字 4 个字节。)
  • 实际上,不要使用uint. 将您的数字 7 位存储到一个字节,其余位用于表示“此数字中有更多字节”。(然后 1-127 适合 1 个字节,128-~16k 适合 2 个字节,~16k-~2M 适合 3 个字节,~2M-~270M 适合 4 个字节。)

这应该会将您的存储空间从每个数字 8 个字节(如果您最初将它们存储为longs)减少到平均 3 个字节。此外,如果您最终需要更大的数字,可变字节存储将能够容纳它们。

然后我可以想出一些方法来进一步减少它,因为你知道数字总是在增加并且可能包含很多运行。哪种方法最适合您,只有您通过在实际数据上尝试才能知道。

  • 对于每个实际数字,存储两个数字:数字本身,后跟连续的数字数(例如2,3,4,5,6=> 2,4)。您必须存储单独的数字,例如8,0这样会增加这些数据的存储空间,但如果您的数据有很多运行(尤其是长数据),这应该会平均减少存储空间。您可以在运行中进一步存储“单个间隙”,例如1,2,3,5,6,7=> 1,6,4(明确,因为4太小而不能成为下一次运行的开始),但这会使处理更加复杂并且不会节省太多空间,所以我不会打扰。
  • 或者,与其存储数字本身,不如存储增量(所以3,4,5,7,8,9=> 3,1,1,2,1,1。这将减少用于存储较大数字的字节数(例如15000,15005(4 个字节)=> 15000,5(3 个字节))。此外,如果数据包含很多运行(例如很多1字节),然后它会很好地压缩(例如zip)。

代码中的处理

我只是建议您编写几个方法,将文件从磁盘流式传输到一个IEnumerable<uint>(或者ulong如果您最终得到更大的数字),然后执行相反的操作,同时处理您从上面实现的任何内容。

如果您以一种懒惰的方式执行此操作 - 使用yield return在从磁盘读取它们并计算它们时返回数字,并将数字流式传输到磁盘而不是将它们保存在内存中并立即返回它们,您可以降低内存使用量存储数据的大小。

(我认为,但我不确定,即使是GZipStream和其他压缩流也可以让您流式传输数据,而无需将其全部存储在内存中。)

查询

如果您要比较两个大数据集,我不建议使用 LINQ 的Intersect方法,因为它需要将其中一个源完全读入内存。但是,您知道两个序列都在增加,您可以编写一个类似的方法,只需要为每个序列保存一个枚举器。

如果您要针对用户输入的小数字列表查询其中一个数据集,您可以愉快地使用Intersect当前实现的 LINQ 方法,因为它只需要第二个序列完全在内存中。

于 2012-09-28T07:37:26.713 回答