如何在文件中密集存储大数字?

时间:2017-08-07 19:33:17

标签: python python-3.x

我需要存储和处理大量非常长的数字,范围从0到64次(ffffffffff ..... ffff)。

如果我将这些数字存储在一个文件中,每个字符(数字)需要1个字节,\ n符号需要2个字节=最多66个字节。然而,要表示所有可能的数字,我们需要不超过34个字节(4位表示从0到f的数字,因此4 [位] * 64 [十六进制数量] / 8 [位a字节] = 32字节+ \ n ,当然)。

有没有办法存储数字而不消耗多余的内存?

到目前为止,我已经创建了从十六进制转换器(每个符号16位)转换为基数为76(十六进制+所有字母和其他一些符号)的数字,这将一个数字的大小减少到41 + 2个字节。

3 个答案:

答案 0 :(得分:6)

您尝试存储长度为32个字节。为什么不将它们存储为二进制数?这样你需要每个数字只存储32个字节而不是41或者其他什么。您可以添加各种准压缩方案,以利用大多数短于32个字节的数据。

如果您的号码是字符串,请先将其转换为int。 Python3 int基本上是无限精度,所以你不会丢失任何信息:

>>> num = '113AB87C877AAE3790'
>>> num = int(num, 16)
>>> num
317825918024297625488

现在您可以将结果转换为字节数组并将其写入为二进制写入打开的文件:

with open('output.bin', 'wb') as file:
    file.write(num.to_bytes(32, byteorder='big'))

int方法to_bytes将您的号码转换为可放置在文件中的字符串。您需要指定字符串长度和顺序。 'big'可以更轻松地读取文件的十六进制转储。

要回读文件并使用int.from_bytes以类似的方式对其进行解码:

with open('output.bin', 'rb') as file:
    bytes = file.read(32)
    num = int.from_bytes(bytes, byteorder='big')

请记住始终在文件模式中包含b,否则如果您尝试使用\n中的代码读取或写入数据,则可能会遇到意外问题。

读取和写入操作都可以循环使用。

答案 1 :(得分:4)

如果您预计存储均匀分布的数字,请参阅Mad Physicist的回答。但是,如果您预计存储的数量很少,但需要能够存储一些大数字,那么这些方案也可能有用。

如果您只需要考虑长度为255或更少字节(2040或更少位)的整数,那么只需将int转换为bytes对象并将其存储在另外一个字节,像这样:

# This was only tested with non-negative integers!
def encode(num):
    assert isinstance(num, int)
    # Convert the number to a byte array and strip away leading null bytes.
    # You can also use byteorder="little" and rstrip.
    # If the integer does not fit into 255 bytes, an OverflowError will be raised.
    encoded = num.to_bytes(255, byteorder="big").lstrip(b'\0')
    # Return the length of the integer in the first byte, followed by the encoded integer.
    return bytes([len(encoded)]) + encoded
def encode_many(nums):
    return b''.join(encode(num) for num in nums)
def decode_many(byte_array):
    assert isinstance(byte_array, bytes)
    result = []
    start = 0
    while start < len(byte_array):
        # The first byte contains the length of the integer.
        int_length = byte_array[start]
        # Read int_length bytes and decode them as int.
        new_int = int.from_bytes(byte_array[(start+1):(start+int_length+1)], byteorder="big")
        # Add the new integer to the result list.
        result.append(new_int)
        start += int_length + 1
    return result

要存储(实际上)无限长度的整数,您可以使用此方案,基于MIDI文件格式的可变长度数量。首先,规则:

  • 一个字节有八位(对于那些不知道的人)。
  • 在除之外的每个字节中,最左边的位(最高位)将为1
  • 每个字节中的低7位(即除最左位之外的所有位)在连接在一起时形成一个具有可变位数的整数。

以下是一些例子:

    二进制文件中的
  • 000000000。它可以用一个字节表示而不需要修改为00000000
  • 二进制文件中的
  • 12701111111。它可以用一个字节表示而不需要修改为01111111
  • 二进制文件中的
  • 12810000000。必须将其转换为双字节表示形式:10000001 00000000。让我们打破这个:
    • 第一个字节中最左边的位是1,这意味着不是最后一个字节。
    • 第二个字节中最左边的位是0,这意味着它是最后一个字节。
    • 第一个字节中的低7位是0000001,第二个字节中的低7位是0000000。将它们连接在一起,得到00000010000000,即128。
  • 二进制文件中的
  • 173249806138790100111011001000111011101001001101111110110100110
    • 存储它:
      • 首先,将二进制数拆分为七位组:0100111 0110010 0011101 1101001 0011011 1111011 0100110(已添加前导0
      • 然后,在除了最后一个字节之外的每个字节前面添加1,这得到010100111 10110010 10011101 11101001 10011011 11111011 00100110
    • 要检索它:
      • 首先,删除每个字节的第一位:0100111 0110010 0011101 1101001 0011011 1111011 0100110
      • 您将获得一个包含七位段的数组。加入他们:100111011001000111011101001001101111110110100110
      • 当转换为十进制时,您将获得173,249,806,138,790。

为什么,你问,我们是否在每个数字的最后一个字节中最左边的位0?好吧,这样做可以让你在不使用换行符的情况下将多个数字连接在一起。将数字写入文件时,只需一个接一个地写入。从文件中读取数字时,使用构建整数数组的循环,每当检测到最左边的位为0的字节时结束每个整数。

以下是两个函数encodedecode,它们在Python 3中的intbytes之间进行转换。

# Important! These methods only work with non-negative integers!
def encode(num):
    assert isinstance(num, int)
    # If the number is 0, then just return a single null byte.
    if num <= 0:
        return b'\0'
    # Otherwise...
    result_bytes_reversed = []
    while num > 0:
        # Find the right-most seven bits in the integer.
        current_seven_bit_segment = num & 0b1111111
        # Change the left-most bit to a 1.
        current_seven_bit_segment |= 0b10000000
        # Add that to the result array.
        result_bytes_reversed.append(current_seven_bit_segment)
        # Chop off the right-most seven bits.
        num = num >> 7
    # Change the left-most bit in the lowest-order byte (which is first in the list) back to a 0.
    result_bytes_reversed[0] &= 0b1111111
    # Un-reverse the order of the bytes and convert the list into a byte string.
    return bytes(reversed(result_bytes_reversed))
def decode(byte_array):
    assert isinstance(byte_array, bytes)
    result = 0
    for part in byte_array:
        # Shift the result over by seven bits.
        result = result << 7
        # Add in the right-most seven bits from this part.
        result |= (part & 0b1111111)
    return result

以下是使用int s列表的两个函数:

def encode_many(nums):
    return [encode(num) for num in nums]
def decode_many(byte_array):
    parts = []
    # Split the byte array after each byte where the left-most bit is 0.
    start = 0
    for i, b in enumerate(byte_array):
        # Check whether the left-most bit in this byte is 0.
        if not (b & 0b10000000):
            # Copy everything up to here into a new part.
            parts.append(byte_array[start:(i+1)])
            start = i + 1
    return [decode(part) for part in parts]

答案 2 :(得分:2)

在不知道更多数字的情况下,最密集的方法是每个数字256位(32字节)。

您可以将它们紧接着存放。

写入文件的函数可能如下所示:

([^.?!]*(\bthis|that\b){2,}[^.?!]*[.|!|?]+)

要阅读这些数字,你可以制作这样的函数:

def write_numbers(numbers, file):
    for n in numbers:
        file.write(n.to_bytes(32, 'big'))

with open('file_name', 'wb') as f:
    write_numbers(get_numbers(), f)