2017-08-07 93 views
1

我需要存儲和處理大量的非常長的數字,範圍從0到f 64倍(ffffffffff ..... ffff)。如何在文件中密集存儲大量數字?

如果我在文件中存儲這些數據,我需要爲每個字符(數字)用於\ N個碼元=高達66個字節1個字節+ 2個字節。但是,爲了表示所有可能的數字,我們不需要多於34個字節(4位表示從0到f的數字,因此4 [位] * 64 [十六進制數量]/8 [位a的字節] = 32字節+ \ n , 當然)。

有什麼辦法來存儲數量,而不消耗過多的內存?

到目前爲止,我已經創建了十六進制(每個符號16位數)的轉換爲基數爲76(十六進制+所有字母和其他符號)的數字,這將數字的大小減少到41 + 2個字節。

+1

無關記:'\ n'只需要一個字節。在Windows上,行尾是'\ r \ n',它需要兩個字節。 – wecsam

+1

你有沒有考慮將文件存儲爲二進制文件? –

+1

相關備註:您可能想了解MIDI格式如何存儲數字。基本上,有一個字節數組,但只使用每個字節的低七位。這些七位段被連接在一起形成大整數。除了最後一個字節以外的所有字節的最高位爲0(或者也許是另一種方式......我不記得了)。全部0的所有連續字節從左側省略。 – wecsam

回答

6

您正在試圖存儲32個字節長。爲什麼不把它們存儲爲二進制數字?這樣你只需要存儲每個數字32個字節而不是41個或其他。您可以添加各種準壓縮方案,以利用諸如大多數數字少於32個字節的情況。

如果您的號碼是一個字符串,將其轉換爲int第一。 Python3 int s爲基本無限的精度,這樣你就不會丟失任何信息:

>>> 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的數據。

無論是讀取和寫入操作可以循環作爲理所當然的事。

2

不知道更多的關於數字將是每數256個比特(32個字節)的最密集的可能的方式。

您可以將它們一個接一個地存儲起來。

一個函數寫一個文件可能是這樣的:

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) 

,並讀取數據,你可以做這樣的功能:

def read_numbers(file): 
    while True: 
     read = file.read(32) 
     if not read: 
      break 
     yield int.from_bytes(read, 'big') 

with open('file_name', 'rb') as f: 
    for n in read_numbers(f): 
     do_stuff(n) 
4

如果您預計存儲的偶數分佈,然後看看瘋狂的物理學家的回答。但是,如果您預計主要存儲小數字,但需要存儲少量大數字,那麼這些方案可能也很有用。

如果只需要以考慮是255首或更少個字節(2040位或更少位)長,然後簡單地轉換intbytes對象並存儲長度中的附加字節,像這樣的整數:

# 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文件格式中的可變長度量。首先,規則:

  • 一個字節有8位(對於那些誰不知道)。
  • 在除最後一個以外的每個字節中,最左邊的位(最高位位)將爲1
  • 當連接在一起時,每個字節中的較低七位(即除最左邊的位以外的所有位)形成具有可變數量位的整數。

這裏有幾個例子:

  • 0二進制是00000000。它可以用一個字節表示而不用修改爲00000000
  • 127二進制是01111111。它可以表示爲一個字節而不需要修改爲01111111
  • 128二進制是10000000。必須將其轉換爲兩字節表示形式:10000001 00000000。讓我們打破下來:
    • 第一個字節的最左邊位爲1,這意味着它是最後一個字節。
    • 第二個字節中最左邊的位是0,這意味着它是最後一個字節。
    • 第一個字節的低七位是0000001,第二個字節的低七位是0000000。這些串聯在一起,你會得到00000010000000,這是128
  • 173249806138790二進制是100111011001000111011101001001101111110110100110
    • 存儲它:
      • 首先,分割二進制數到的七個比特組:0100111 0110010 0011101 1101001 0011011 1111011 0100110(領先0加入)
      • 然後,在每個字節前面添加一個1除了最後,這得到了010100111 10110010 10011101 11101001 10011011 11111011 00100110
    • 找回它:
      • 首先,刪除每個字節的第一位:0100111 0110010 0011101 1101001 0011011 1111011 0100110
      • 您剩下一個由7位段組成的數組。將它們加在一起:100111011001000111011101001001101111110110100110
      • 當它被轉換成十進制時,你得到173,249,806,138,790。

爲什麼,你問,我們讓每個號碼0的最後一個字節最左邊的位?那麼,這樣做可以讓你連接多個數字而不用換行符。將數字寫入文件時,只需將它們一個接一個地寫出來。當從文件中讀取數字時,使用一個循環構建一個整數數組,每當它檢測到最左邊的位爲0的字節時結束每個整數。

這裏有兩個功能,encodedecode,這intbytes之間的轉換在Python 3

# 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小號列出工作:

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] 
+0

問題有[python]標籤。沒有Python代碼? – gboffi

+1

你最好只在一個單獨的字節中編碼長度。平均情況下,從每個字節取一位需要2個字節,而任何數目最多爲32的位都可以很容易地放入5位。 –

+0

@gboffi我在Python 3中編寫了一些示例代碼,並將其添加到我的答案中。 – wecsam