csv文件压缩而不使用Python中的现有库

时间:2016-07-28 21:45:00

标签: python csv compression

我试图在不使用任何第三方或框架提供的压缩库的情况下压缩.csv文件。

我已经尝试过,我想要的是什么,一切。 我看着赫夫曼,但由于我不允许使用那个解决方案,所以我试着自己做。

一个例子:

6NH8,F,A,0,60541567,60541567,78.78,20
6NH8,F,A,0,60541569,60541569,78.78,25
6AH8,F,B,0,60541765,60541765,90.52,1
QMH8,F,B,0,60437395,60437395,950.5,1

我制作了一个算法,对每个字符进行计数,并给出了他们使用过的次数,并取决于他们专注于一个数字的时间。

',' --- 28  
'5' --- 18
'6' --- 17 
'0' --- 15
'7' --- 10  
'8' --- 8 
'4' --- 8  
'1' --- 8
'9' --- 6  
'.' --- 4
'3' --- 4 
'\n'--- 4 
'H' --- 4    
'F' --- 4
'2' --- 3 
'A' --- 3
'N' --- 2 
'B' --- 2 
'M' --- 1 
'Q' --- 1 

[(',', 0), ('5', 1), ('6', 2), ('0', 3), ('7', 4), ('8', 5), 
 ('4', 6), ('1', 7), ('9', 8), ('.', 9), ('3', 10), ('\n', 11), 
 ('H', 12), ('F', 13), ('2', 14), ('A', 15), ('N', 16), ('B', 17), 
 ('M', 18), ('Q', 19)]

因此,不是存储例如ord(' H')= 72,而是给H值12,依此类推。

但是,当我将所有字符更改为我的值时,我生成的cvs(> 40MB)仍然大于原始字符(19MB)。

我甚至尝试了将列表分成2的替代方案。即,对于一行,使其成为两行。

[6NH8,F,A,0,]
[60541567,60541567,78.78,20]

但仍然比我的"霍夫曼更大,甚至更大。版本

问题: 有人对如何做任何建议 1.读一个.csv文件, 2.使用lib这样的东西。或第三方。 3.生成并写一个较小的.csv文件?

对于第2步我不要求完整的计算解决方案,只是建议如何最小化文件,即将每个值写为一个列表?等

谢谢

3 个答案:

答案 0 :(得分:0)

尝试在每个单元格的内容上运行算法而不是单个字符,然后使用压缩的单元格值创建新的CSV文件。

如果您提供的数据是较大文件的示例,则可能需要分别在每列上运行压缩算法。例如,它可能仅有助于压缩列0,4和5。

要读取和写入CSV文件,请查看csv模块,您可以在其中执行以下操作:

import csv
with open('eggs.csv', 'rb') as csvfile:
    spamreader = csv.reader(csvfile, delimiter=' ', quotechar='|')
    for row in spamreader:
        print ', '.join(row)

答案 1 :(得分:0)

对于每一行,在上一行或多行中搜索匹配的子字符串。对于每个匹配的子字符串(例如6NH8,F,A,0,6054156,78.78,2),请将匹配的长度和距离发送回来复制。这称为LZ77压缩。

答案 2 :(得分:0)

目前还不清楚您是否需要创建一个通用的压缩算法或一个适用于此类数据的自定义压缩算法。

还不清楚输出是否应该是另一个CSV,由可打印的ASCII字符或纯二进制数据组成的字符串。

我将假设我们正在讨论自定义算法和CSV输出。 (无论如何,相同的原则将适用于另一种输出格式。)

您的输入似乎格式正确,并且始终重复相同类型的字段:

0 '6NH8'     : 4-character code
1 'F'        : character
2 'A'        : character
3 '0'        : integer
4 '60541567' : integer \_ some kind of
5 '60541567' : integer /  timestamps?
6 '78.78'    : float
7 '20'       : integer

构建词典

查看第0列中使用了多少个不同的代码,以及“第1列”+“第2列”的不同组合数量。

如果经常使用相同的值,那么构建仅存储一次然后在压缩行中引用的字典绝对值得。

例如:

column0_dictionary = [ '6NH8', '6AH8', 'QMH8' ]
column12_dictionary = [ 'FA', 'FB' ];

因此,6NH8将被引用为06AH8将被引用为1等。

同样,F,A将被引用为0F,B将被引用为1

以较短格式编码时间戳

假设列#4和#5确实是时间戳,快速获胜将存储最小值并从每个压缩行中的实际值中减去它。

minimum_timestamp = 60437395

因此,60541569变为60541569 - 60437395 = 104174。

示例输出

以下是将这两个简单方法应用于示例输入时的结果:

# header
6NH8,6AH8,QMH8
FA,FB
60437395
# payload data
0,0,0,104172,104172,78.78,20
0,0,0,104174,104174,78.78,25
1,1,0,104370,104370,90.52,1
2,1,0,0,0,950.5,1

如果事实证明它们对应于“事物的开始”和“事物的结束”,那么您还可以在第5列中存储第5列和第4列之间的差异。

原样,压缩有效载荷的大小约为原始输入大小的70%。 (请记住,当您有更多行时,标题的大小应该可以忽略不计。)

您的示例太短,无法检测剩余字段的任何其他明显模式,但希望这些示例可以为您提供一些想法。

<强>更新

事实证明,时间戳以自午夜起经过的毫秒数表示。所以它们可能在0-86399999中均匀分布,并且不可能减去最小值。

然而,这些数字可以比其十进制值的ASCII表示更紧凑的方式编码。

最简单的方法是将它们转换为十六进制:

60541567 = 39BCA7F

稍微复杂一点的方法是在Base64中对它们进行编码:

  1. 将时间戳转换为4字节表示形式(0到86399999之间的所有值都适合4个字节):

  2. 构建一个由4个相应字符组成的字符串,并在Base64中对其进行编码。

  3. 例如:

    60541567 = 03 9B CA 7F  # in hexadecimal and big-endian order
    
    BASE64(CHR(0x03) + CHR(0x9B) + CHR(0xCA) + CHR(0x7F)) = A5vKfw
    # here without the padding characters