在Python中保存和加载位/字节

时间:2018-03-15 18:26:46

标签: python arrays python-3.x bits

我最近一直在研究压缩算法,我试图理解如何将整数存储为Python中的位以节省空间。

首先,我将'1'和'0'保存为Python中的字符串。

import os
import numpy as np

array= np.random.randint(0, 2, size = 200)
string = [str(i) for i in array]
with open('testing_int.txt', 'w') as f:
    for i in string:
        f.write(i)

print(os.path.getsize('testing_int.txt'))

我找回200字节是有意义的,因为每个字符在ascii中用一个字节表示(如果字符是拉丁语,则表示utf-8)。

现在如果试图将这些1和0保存为位,我应该只在25 bytes左右?

200 bits/8 = 25 bytes

但是,当我尝试下面的代码时,我会得到105 bytes。 我做错了吗?

使用与上面相同的'数组变量',我尝试了这个:

bytes_string = [bytes(i) for i in array]
with open('testing_bytes.txt', 'wb') as f:
    for i in bytes_string:
        f.write(i)

然后我尝试了这个:

bin_string = [bin(i) for i in array]
with open('testing_bin.txt', 'wb') as f:
    for i in bytes_string:
        f.write(i)

这也会占用105 bytes左右。

所以我试着查看文本文件,我注意到了 'bytes.txt'和'bin.txt'都是空白的。

所以我尝试通过以下代码阅读'bytes.txt'文件:

with open(r"C:\Users\Moondra\Desktop\testing_bytes\testing_bytes.txt", 'rb') as f:
    x =f.read()

现在我回来了:

b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

所以我尝试了这些命令:

>>> int.from_bytes(x, byteorder='big')
0
>>> int.from_bytes(x, byteorder='little')
0
>>> 

显然,我做错了很多事。 我无法弄明白:

1)为什么我没有得到一个25字节的文本文件 2)为什么我可以正确读回字节文件。

谢谢。

2 个答案:

答案 0 :(得分:1)

bytes_string = [bytes(i) for i in array]

您希望bytes(x)能够为您提供值为bytes的单字节x对象。按照documentation进行操作,您会看到bytes()已初始化为bytearray(),而bytearray()会对其参数进行说明:

  

如果是整数,则数组将具有该大小,并将使用空字节进行初始化。

因此bytes(0)为您提供一个空的字节对象,bytes(1)为您提供一个序数为零的单字节。这就是为什么bytes_string大约是array的一半,并且完全由零字节构成。

至于为什么bin()示例不起作用,它看起来像是一个简单的复制粘贴案例而忘记在bytes_string中忘记将bin_string更改为for循环。

这仍然无法实现将0或1值整数视为位的目标。 Python并没有真正内置的那种功能。有第三方模块允许你在位级工作,但我不能特别说它们中的任何一个。就个人而言,我可能只是将自己的具体内容推广到应用程序。

答案 1 :(得分:1)

看起来你正试图将所有值都移位到一个字节。例如,您希望将整数值[0,1,0,1,0,1,0,1]打包成一个类似于以下二进制数的字节:0b01010101。为此,您需要使用按位移位运算符和按位或运算符以及struct模块将值打包到unsigned Char中,该Char表示您拥有的int值序列。

下面的代码获取范围[0,1]中的随机整数数组,并将它们一起移位以生成可以打包成单个字节的二进制数。为方便起见,我使用了256个整数。然后,文件的预期字节数为32(256/8)。你会看到,当它运行时,这确实是你得到的。

import struct
import numpy as np
import os

a = np.random.randint(0, 2, size = 256)
bool_data = []

bin_vals = []
for i in range(0, len(a), 8):
    bin_val = (a[i] << 0) | (a[i+1] << 1) | \
    (a[i+2] << 2) | (a[i+3] << 3) | \ 
    (a[i+4] << 4) | (a[i+5] << 5) | \
    (a[i+6] << 6) | (a[i+7] << 7)
    bin_vals.append(struct.pack('B', bin_val))

with open("output.txt", 'wb') as f:
    for val in bin_vals:
        f.write(val)

print(os.path.getsize('output.txt'))

但请注意,这仅适用于[0,1]范围内的整数值,因为如果它们更大,它将移动更多的非零并破坏生成的字节的结构。在这种情况下,二进制数的大小也可能超过1个字节。

看起来你只是在使用python试图为演示目的生成一个位数组,对于那个标记,我会说python可能不是最适合这个。我建议使用较低级别的语言,例如C / C ++,它比python更直接地访问数据类型。