编写 Python 程序,实现利用霍夫曼编码对文本文件的压缩
时间: 2023-06-19 09:09:09 浏览: 138
以下是一个基本的 Python 程序,用于利用霍夫曼编码对文本文件进行压缩:
```python
import heapq
import os
class HuffmanCoding:
def __init__(self, path):
self.path = path
self.heap = []
self.codes = {}
self.reverse_mapping = {}
class HeapNode:
def __init__(self, char, freq):
self.char = char
self.freq = freq
self.left = None
self.right = None
def __lt__(self, other):
return self.freq < other.freq
def __eq__(self, other):
if(other == None):
return False
if(not isinstance(other, HeapNode)):
return False
return self.freq == other.freq
def make_frequency_dict(self, text):
frequency = {}
for character in text:
if not character in frequency:
frequency[character] = 0
frequency[character] += 1
return frequency
def make_heap(self, frequency):
for key in frequency:
node = self.HeapNode(key, frequency[key])
heapq.heappush(self.heap, node)
def merge_nodes(self):
while(len(self.heap)>1):
node1 = heapq.heappop(self.heap)
node2 = heapq.heappop(self.heap)
merged = self.HeapNode(None, node1.freq + node2.freq)
merged.left = node1
merged.right = node2
heapq.heappush(self.heap, merged)
def make_codes_helper(self, root, current_code):
if(root == None):
return
if(root.char != None):
self.codes[root.char] = current_code
self.reverse_mapping[current_code] = root.char
return
self.make_codes_helper(root.left, current_code + "0")
self.make_codes_helper(root.right, current_code + "1")
def make_codes(self):
root = heapq.heappop(self.heap)
current_code = ""
self.make_codes_helper(root, current_code)
def get_encoded_text(self, text):
encoded_text = ""
for character in text:
encoded_text += self.codes[character]
return encoded_text
def pad_encoded_text(self, encoded_text):
extra_padding = 8 - len(encoded_text) % 8
for i in range(extra_padding):
encoded_text += "0"
padded_info = "{0:08b}".format(extra_padding)
encoded_text = padded_info + encoded_text
return encoded_text
def get_byte_array(self, padded_encoded_text):
if(len(padded_encoded_text) % 8 != 0):
print("Encoded text not padded properly")
exit(0)
b = bytearray()
for i in range(0, len(padded_encoded_text), 8):
byte = padded_encoded_text[i:i+8]
b.append(int(byte, 2))
return b
def compress(self):
filename, file_extension = os.path.splitext(self.path)
output_path = filename + ".bin"
with open(self.path, 'r+') as file, open(output_path, 'wb') as output:
text = file.read()
text = text.rstrip()
frequency = self.make_frequency_dict(text)
self.make_heap(frequency)
self.merge_nodes()
self.make_codes()
encoded_text = self.get_encoded_text(text)
padded_encoded_text = self.pad_encoded_text(encoded_text)
b = self.get_byte_array(padded_encoded_text)
output.write(bytes(b))
print("Compressed")
return output_path
""" functions for decompression """
def remove_padding(self, padded_encoded_text):
padded_info = padded_encoded_text[:8]
extra_padding = int(padded_info, 2)
padded_encoded_text = padded_encoded_text[8:]
encoded_text = padded_encoded_text[:-extra_padding]
return encoded_text
def decode_text(self, encoded_text):
current_code = ""
decoded_text = ""
for bit in encoded_text:
current_code += bit
if(current_code in self.reverse_mapping):
character = self.reverse_mapping[current_code]
decoded_text += character
current_code = ""
return decoded_text
def decompress(self, input_path):
filename, file_extension = os.path.splitext(self.path)
output_path = filename + "_decompressed" + ".txt"
with open(input_path, 'rb') as file, open(output_path, 'w') as output:
bit_string = ""
byte = file.read(1)
while(byte != b""):
byte = ord(byte)
bits = bin(byte)[2:].rjust(8, '0')
bit_string += bits
byte = file.read(1)
encoded_text = self.remove_padding(bit_string)
decompressed_text = self.decode_text(encoded_text)
output.write(decompressed_text)
print("Decompressed")
return output_path
```
首先,我们定义了一个名为 HuffmanCoding 的类。在类的初始化中,我们将给定的文件路径存储到 self.path 中,并初始化一个空堆(用于构建霍夫曼树),一个空字典(用于存储字符的霍夫曼编码),以及一个空字典(用于反向映射霍夫曼编码)。
接下来,我们定义了一个名为 HeapNode 的嵌套类。这个类代表了堆中的一个节点,它存储了节点代表的字符和出现的频率,以及左右子节点的指针。
接着,我们定义了一个名为 make_frequency_dict 的函数。这个函数接受一个字符串作为输入,并返回一个字典,其中键是字符串中出现的每个字符,而值是该字符在字符串中出现的次数。
然后,我们定义了一个名为 make_heap 的函数。这个函数接受一个字典作为输入,并用字典中的每个键值对创建一个 HeapNode 对象。然后,它将这些节点添加到堆中,以便可以构建霍夫曼树。
接下来,我们定义了一个名为 merge_nodes 的函数。这个函数使用堆中的节点来构建霍夫曼树。它从堆中弹出两个最小频率的节点,并使用它们创建一个新的节点。然后,它将这个新节点添加回堆中,直到堆中只剩下一个节点。
接着,我们定义了一个名为 make_codes_helper 的递归函数。这个函数从根节点开始遍历霍夫曼树,直到找到叶子节点。在遍历树的过程中,它将节点的编码添加到 self.codes 中,并将编码和字符的反向映射添加到 self.reverse_mapping 中。
然后,我们定义了一个名为 make_codes 的函数。这个函数使用霍夫曼树的根节点调用 make_codes_helper,以便遍历整个树并生成编码。
接下来,我们定义了一个名为 get_encoded_text 的函数。这个函数接受一个字符串作为输入,并返回一个字符串,其中每个字符都用它的霍夫曼编码替换。
然后,我们定义了一个名为 pad_encoded_text 的函数。这个函数接受一个字符串作为输入,并返回一个新字符串。新字符串的长度是 8 的倍数,因为我们需要确保最后一个字节中的位数是完全填充的。为了实现这一点,我们需要添加一些额外的位数,以便使字符串的长度成为 8 的倍数。我们还在字符串的开头添加一个 8 位二进制数,以表示我们添加了多少额外的位数。
接着,我们定义了一个名为 get_byte_array 的函数。这个函数接受一个字符串作为输入,并将其转换为字节数组。我们首先检查字符串的长度是否是 8 的倍数,如果不是,则输出错误信息并退出程序。然后,我们将字符串分成每 8 个字符一组,并将每组转换为一个字节。最后,我们将所有字节组合成一个字节数组并返回它。
接下来,我们定义了一个名为 compress 的函数。这个函数读取输入文件的内容,并使用之前定义的函数来执行以下步骤:
1. 使用 make_frequency_dict 创建一个频率字典。
2. 使用 make_heap 创建一个堆。
3. 使用 merge_nodes 创建一个霍夫曼树。
4. 使用 make_codes 创建字符的编码。
5. 使用 get_encoded_text 将文件内容编码为霍夫曼编码。
6. 使用 pad_encoded_text 填充霍夫曼编码。
7. 使用 get_byte_array 将霍夫曼编码转换为字节数组。
8. 将字节数组写入输出文件。
最后,我们定义了一个名为 decompress 的函数。这个函数接受一个输入文件的路径作为输入,并使用之前定义的函数来执行以下步骤:
1. 读取输入文件的内容,将其转换为一个二进制字符串。
2. 使用 remove_padding 去除字符串的填充位。
3. 使用 decode_text 将霍夫曼编码解码为原始文本。
4. 将解码后的文本写入输出文件。
使用这个程序,我们可以压缩任何文本文件。例如,我们可以使用以下代码来压缩名为 input.txt 的文件:
```python
h = HuffmanCoding("input.txt")
h.compress()
```
这将生成一个名为 input.bin 的压缩文件。要解压缩这个文件,我们可以使用以下代码:
```python
h = HuffmanCoding("input.bin")
h.decompress("input.bin")
```
这将生成一个名为 input_decompressed.txt 的解压缩文件。
阅读全文