Кодирование Хаффмана: как записать двоичные данные в Python - PullRequest
3 голосов
/ 21 июля 2011

Я пробовал методы, использующие модуль struct, как показано строками, закомментированными в моем коде, но это не сработало. По сути, у меня есть два варианта: я могу либо написать двоичный код данных по коду (мой код представляет собой последовательности битов длиной от 3 до 13 бит), либо преобразовать целую строку из n символов (в данном случае n = 25000 +) в двоичные данные. Но я не знаю, как реализовать оба метода. Код:

import heapq
import binascii
import struct

def createFrequencyTupleList(inputFile):
    frequencyDic = {}

    intputFile = open(inputFile, 'r')
    for line in intputFile:
        for char in line:
            if char in frequencyDic.keys():
                frequencyDic[char] += 1
            else:
                frequencyDic[char] = 1

    intputFile.close()
    tupleList = []
    for myKey in frequencyDic:
        tupleList.append((frequencyDic[myKey],myKey))
    return tupleList

def createHuffmanTree(frequencyList):
    heapq.heapify(frequencyList)
    n = len(frequencyList)
    for i in range(1,n):
        left = heapq.heappop(frequencyList)
        right = heapq.heappop(frequencyList)
        newNode = (left[0] + right[0], left, right)
        heapq.heappush(frequencyList, newNode)
    return frequencyList[0]

def printHuffmanTree(myTree, someCode,prefix=''):
    if len(myTree) == 2:
        someCode.append((myTree[1] + "@" + prefix))
    else:
        printHuffmanTree(myTree[1], someCode,prefix + '0')
        printHuffmanTree(myTree[2], someCode,prefix + '1')

def parseCode(char, myCode):
    for k in myCode:
        if char == k[0]:
            return k[2:]


if __name__ == '__main__':
    myList = createFrequencyTupleList('input')
    myHTree = createHuffmanTree(myList)
    myCode = []
    printHuffmanTree(myHTree, myCode)
    inputFile = open('input', 'r')
    outputFile = open('encoded_file2', "w+b")
    asciiString = ''
    n=0
    for line in inputFile:
        for char in line:
            #outputFile.write(parseCode(char, myCode))
            asciiString += parseCode(char, myCode)
            n += len(parseCode(char, myCode))
    #values = asciiString
    #print n
    #s = struct.Struct('25216s')
    #packed_data = s.pack(values)
    #print packed_data
    inputFile.close()
    #outputFile.write(packed_data)
    outputFile.close()

1 Ответ

4 голосов
/ 21 июля 2011

Вы ищете это:

packed_data = ''.join(chr(int(asciiString[i:i+8], 2)) 
                         for i in range(0, len(asciiString), 8))

Он будет принимать 8 бит за раз от asciiString, интерпретировать его как целое число и выводить соответствующий байт.

Ваша проблема здесь в том, что для правильной работы требуется, чтобы длина asciiString была кратна 8 битам.Если нет, вы вставите нулевые биты перед последними несколькими действительными битами.

Так что вам нужно где-то хранить количество бит в последнем байте, чтобы вы знали, что игнорировать эти биты, когда получите их обратно,вместо того, чтобы интерпретировать их как нули.Вы можете попробовать:

packed_data = chr(len(asciiString) % 8) + packed_data

Затем, когда вы читаете это обратно:

packed_input = coded_file.read()
last_byte_length, packed_input, last_byte = (packed_input[0], 
                                             packed_input[1:-1], 
                                             packed_input[-1])
if not last_byte_length: last_byte_length = 8
ascii_input = ''.join(chain((bin(ord(byte))[2:].zfill(8) for byte in packed_input),
                      tuple(bin(ord(last_byte))[2:].zfill(last_byte_length),)))
# OR
# ascii_input = ''.join(chain(('{0:0=8b}'.format(byte) for byte in packed_input),
#                       tuple(('{0:0=' + str(last_byte_length) + '8b}').format(last_byte),)))

Редактировать: вам нужно либо удалить '0b' из строк, возвращаемых bin(), либо, на2.6 или новее, желательно использовать новые, альтернативные версии, которые я добавил, которые используют форматирование строк вместо bin(), нарезку и zfill().

Редактировать: Спасибо, eryksun, хорошо использовать цепочку, чтобы избежать копированиястроки ASCII.Также необходимо позвонить ord(byte) в bin() версии.

...