Python LZ77 - алгоритм сжатия
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# подключаемые модули
import math
# константы
tMethod = 'LZ77'
sizeSymbol = 8 # размер 1го символа в битах
# функции
def readInputText(fileName):
tInput, nD, nB = '', 8, 5
try:
# пытаемся открыть файл на чтение
f = open(fileName, 'rt', encoding='utf-8')
except FileNotFoundError:
# файл не найден - запрашиваем параметры с консоли
tInput = input('\n введите исходное сообщение -').strip()
try:
nD = int(input('размер словаря (сим, default=8)-').strip())
except ValueError:
nD = 8
try:
nB = int(input('размер буфера (сим, default=5)-').strip())
except ValueError:
nB = 5
else:
# файл найден чтаем параметры из файла
tInputLines = f.readlines()
f.close()
for tLine in tInputLines:
if tLine[0:11] == 'tInputText=':
tInput = tLine[11:].strip()
elif tLine[0:6] == 'nDict=':
try:
nD = int(tLine[6:].strip())
except ValueError:
nD = 8
elif tLine[0:6] == 'nBuff=':
try:
nB = int(tLine[6:].strip())
except ValueError:
nB = 8
finally:
# выводим параметры на консоль
print('\n исхоное сообщение - {0}'.format(tInput))
print('размер словаря - {0} сим.'.format(nD))
print('размер буфера - {0} сим.'.format(nB))
# сохраняем параметры в файл
with open(fileName, 'wt', encoding='utf-8') as f:
f.write('tInputText={0}\n'.format(tInput))
f.write('nDict={0}\n'.format(nD))
f.write('nBuff={0}\n'.format(nB))
return tInput, nD, nB
# используем 36-ричную систему счисления для смещения и длины строки
def str36(num36):
if (num36 >= 0) and (num36 <= 9):
strNum = str(num36)
elif (num36 >= 10) and (num36 <= 36):
strNum = chr(0x0041 + num36 - 10) # ord('A') = 0x0041
else:
strNum = 'Error'
return strNum
def makeLZ77(d, ld, b, lb):
i = d.find(b[0])
# первый символ буфера в словаре не найден или это последний символ буфера
if (i == -1) or (i == len(b) - 1):
i = 0 # смещение
n = 0 # длина подстроки
Code = str36(i) + str36(n) + b[n] # код lz77
# первый символ буфера в словаре найден смещение - i
else:
n = 0 # длина подстроки
while (n < len(d) - i) and (n < len(b)) and (b[n] == d[i + n]):
# if n <= len(b):
n += 1
Code = str36(i) + str36(n) + b[n] # код lz77
sizeCode = ld + lb + sizeSymbol # размер 1го lz77 кода в битах
# сдвиг курсора код и размер кода
return [n + 1, Code, sizeCode]
#####
def makeOutputText(fileName, tInput, nD, nB):
print('\n' + 'метод {0}'.format(tMethod).center(max(nD, 7) + max(nB, 5) + 3 + 10))
print('=={0}==={1}==={2}=='.format(''.ljust(max(nD, 7), '='), ''.ljust(max(nB, 5), '='), ''.ljust(3, '=')))
print('| {0} | {1} | {2} |'.format('словарь'.center(max(nD, 7)), 'буфер'.center(max(nB, 5)), 'код'.center(3)))
print('|-{0}-|-{1}-|-{2}-|'.format(''.ljust(max(nD, 7), '-'), ''.ljust(max(nB, 5), '-'), ''.ljust(3, '-')))
f = open(fileName, 'wt', encoding='utf-8')
f.write('метод {0}'.format(tMethod).center(max(nD, 7) + max(nB, 5) + 3 + 10) + '\n')
f.write('=={0}==={1}==={2}==\n'.format(''.ljust(max(nD, 7), '='), ''.ljust(max(nB, 5), '='), ''.ljust(3, '=')))
f.write('| {0} | {1} | {2} |\n'.format('словарь'.center(max(nD, 7)), 'буфер'.center(max(nB, 5)), 'код'.center(3)))
f.write('|-{0}-|-{1}-|-{2}-|\n'.format(''.ljust(max(nD, 7), '-'), ''.ljust(max(nB, 5), '-'), ''.ljust(3, '-')))
nInput = len(tInput)
tOutput, sizeOutput = '', 0
k = 0
while k < nInput:
if k < nD: # определение содержимого словаря
tDict = tInput[0:k]
else:
tDict = tInput[k - nD:k]
tDict = tDict.rjust(nD, chr(0x02f3))
# символ пустого месте также - 0x02da
# определение содержимого буфера
tBuff = tInput[k:k + nB]
# получение сдвига курсора кода и его размера в битах
if tMethod == 'LZ77':
n, tCode, sizeCode = makeLZ77(tDict, math.ceil(math.log2(nD)), tBuff, math.ceil(math.log2(nB)))
print('| {0} | {1} | {2} |'.format(tDict.ljust(max(nD, 7)), \
tBuff.ljust(nB, chr(0x02da)).ljust(max(nB, 5)), tCode.ljust(3)))
f.write('| {0} | {1} | {2} |'.format(tDict.ljust(max(nD, 7)), \
tBuff.ljust(nB, chr(0x02da)).ljust(max(nB, 5)), tCode.ljust(3)))
tOutput += tCode
sizeOutput += sizeCode
k += n
# input()
print('=={0}==={1}==={2}=='.format(''.ljust(max(nD, 7), '='), ''.ljust(max(nB, 5), '='), ''.ljust(3, '=')))
f.write('=={0}==={1}==={2}==\n'.format(''.ljust(max(nD, 7), '='), ''.ljust(max(nB, 5), '='), ''.ljust(3, '=')))
f.close()
return tOutput, sizeOutput
def writeOutputText(fileName, tOutput, sizeOutput, nInput, nD):
print('\n сжатое (по методу {0}) сообщение - {1}'.format(tMethod, tOutput))
print('\n размер сжатого сообщения - {0} бит.'.format(sizeOutput))
print('коэффициент сжатия - {0}.'.format(sizeOutput / (sizeSymbol * nInput)))
with open(fileName, 'wt', encoding='utf-8') as f:
f.write('tMethod={0}\n'.format(tMethod))
f.write('tOutputText={0}\n'.format(tOutput))
f.write('nDict={0}\n'.format(nD))
f.write('\n размер сжатого сообщения - {} бит. \n'.format(sizeOutput))
f.write('коэффициент сжатия - {0}.'.format(sizeOutput / (sizeSymbol * nInput)))
return
# -*- Main -*-
# прочитаем исходное сообщение
tInputText, nDict, nBuff = readInputText('Lab2-1.txt')
# создадим сжатый текст
tOutputText, sizeOutputText = makeOutputText('Lab2-2.txt', tInputText, nDict, nBuff)
# сохраним сжатый текст
writeOutputText('Lab2-3.txt', tOutputText, sizeOutputText, len(tInputText), nDict)
Данный код - алгоритм сжатия LZ77. В процессе запуска с дефолтными значаниями (словарь = 8 и буфер = 5) при использовании входного слова (красная_краска) работает отлично, но если взять допустим (словарь = 8, буфер = 5 и вход.слово = красная_краска_красная_краска) то не работает, где то в конце, в методе str36 - ошибка. Не могли бы вы помочь с ее исправлением?
