我需要存储和处理大量非常长的数字,范围从0到64次(ffffffffff ..... ffff)。
如果我将这些数字存储在一个文件中,每个字符(数字)需要1个字节,\ n符号需要2个字节=最多66个字节。然而,要表示所有可能的数字,我们需要不超过34个字节(4位表示从0到f的数字,因此4 [位] * 64 [十六进制数量] / 8 [位a字节] = 32字节+ \ n ,当然)。
有没有办法存储数字而不消耗多余的内存?
到目前为止,我已经创建了从十六进制转换器(每个符号16位)转换为基数为76(十六进制+所有字母和其他一些符号)的数字,这将一个数字的大小减少到41 + 2个字节。
答案 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
。以下是一些例子:
0
为00000000
。它可以用一个字节表示而不需要修改为00000000
。127
为01111111
。它可以用一个字节表示而不需要修改为01111111
。128
为10000000
。必须将其转换为双字节表示形式:10000001 00000000
。让我们打破这个:
1
,这意味着不是最后一个字节。0
,这意味着它是最后一个字节。0000001
,第二个字节中的低7位是0000000
。将它们连接在一起,得到00000010000000
,即128。173249806138790
为100111011001000111011101001001101111110110100110
。
0100111 0110010 0011101 1101001 0011011 1111011 0100110
(已添加前导0
)1
,这得到0
:10100111 10110010 10011101 11101001 10011011 11111011 00100110
0100111 0110010 0011101 1101001 0011011 1111011 0100110
100111011001000111011101001001101111110110100110
为什么,你问,我们是否在每个数字的最后一个字节中最左边的位0
?好吧,这样做可以让你在不使用换行符的情况下将多个数字连接在一起。将数字写入文件时,只需一个接一个地写入。从文件中读取数字时,使用构建整数数组的循环,每当检测到最左边的位为0
的字节时结束每个整数。
以下是两个函数encode
和decode
,它们在Python 3中的int
和bytes
之间进行转换。
# 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)