Что нового

[Данные, строки] Декомпрессия данных частями

joiner

Модератор
Локальный модератор
Сообщения
3,570
Репутация
632
Есть различные вариации функции сжатия бинарных данных
_LZNTCompress - эту функцию можно посмотреть в исходнике Autoit3Wrapper (и не только)
ею можно сжимать бинарные данные , считывая сразу весь файла , можно сжимать, считывая файл частями.
_LZNTDecompress - функция декомпрессии данных. считываем файл, который был сжат предыдущей функцией и получаем исходный файл. но...
нужно считать файл полностью, считывать файл частями и проводить декомпрессию - значит получить на выходе нечитаемый файл.
Когда файл малых размеров, то легко можно считать его полностью и сделать декомпрессию, но если файл приличных размеров, то это значит что произойдет отказ функции. ну это и понятно. память не безразмерна.
Как сделать декомпрессию, считывая файл (сжатый) частями, а не целиком?Я не могу понять. скорее всего из-за недостатка каких-то элементарных знаний
во вложении по паре вариантов указанных функций
 

Вложения

  • LZNTCompress.au3
    9.3 КБ · Просмотры: 12

snoitaleR

AutoIT Гуру
Сообщения
855
Репутация
223
joiner
Если алгоритм сжатия последовательный - то, вероятно, никак...
А если алгоритм сжатия блочный, то можно извлечь произвольный блок...
Это мысли-фантазии, так как с терминологией алгоритмов сжатия не знаком...


Добавлено:
Сообщение автоматически объединено:

joiner
Провёл эксперимент: распаковал с помощью WINRAR 10 ГБ архив на компьютере с 1 ГБ оперативной памяти, это оказалось возможным...
Но следует учитывать, что внутри архива не было ни одного файла, превышающего 1 ГБ...
Создал архив объёмом 3 ГБ, внутри один файл объёмом 3 ГБ, удалось его распаковать на компьютере с 1 ГБ оперативной памяти...
По крайней мере, это возможно...
 
Автор
joiner

joiner

Модератор
Локальный модератор
Сообщения
3,570
Репутация
632
при упаковки файла свыше 300мб считывая весь файл вызывало ошибку выделения памяти . я стал считывать такие файлы по мегабайту. все ок. но мегабайт до компресси это уже не мегабайт после.
соответственно каждый мегабайт сжимается по разному. а значит получаем сотни блоков разных размеров. такой файл можно распаковать, если считать его полностью. я так и делал. это не вызывало ошибку выделения памяти, но происходит очень долго.
думал считывать блоками, но на выходе получил нечитаемый файл.
я думаю, что нужно считывать блоками, размеры которых идентичны тем, которые получали после компрессии. но эти данные нужно куда то писать
а это сотня, тысяча блоков, исходя из того, как мы его считывали при компрессии
 

snoitaleR

AutoIT Гуру
Сообщения
855
Репутация
223
joiner
Я думаю, WINRAR использует по-максимуму объём свободной оперативной памяти, или по крайней мере в каких-то разумных пределах...
 
Автор
joiner

joiner

Модератор
Локальный модератор
Сообщения
3,570
Репутация
632
еще раз, проблема не в упаковке. упаковать можно блоками.
проблема в распаковке. распаковать считывая частями и записывая в файл. получить на выходе читаемый файл
 

firex

AutoIT Гуру
Сообщения
943
Репутация
208
joiner
Ну так в чем проблема:

Делите ваш файл на части, сжимаете их. Попутно пишите их в файл в виде:
Код:
$tagMYCOMPRESSEDPART = "dword Header; dword PackLen; byte[" & Binarylen($CompressedPart) & "]"


Далее декомпрессия:
Читаем с файла:
Код:
$tagMYCOMPRESSEDPART_HEADER = "dword Header; dword PackLen;"


Получаем PackLen, читаем:
Код:
$tagMYCOMPRESSEDPART = "byte[" & $PackLen & "]"


Декомпрессим и переходим к следующей части.

Функции:
Код:
_WinAPI_CreateFile
_WinAPI_ReadFile
_WinAPI_WriteFile


P.S. Только для PackLen больше подойдет uint64
 
Автор
joiner

joiner

Модератор
Локальный модератор
Сообщения
3,570
Репутация
632
и на сколько частей делить 1ГБ? считывать по 100мб? если читать по мегабайту, то тысячи блоков.
firex
можно реальный пример?
 

snoitaleR

AutoIT Гуру
Сообщения
855
Репутация
223
joiner
Любопытная информация...
Установил в WINRAR формат архива RAR5, размер словаря 1 ГБ, и WINRAR выдал предупреждение:
Словарь размером 1024 МБ использует очень много памяти для упаковки и распаковки файлов. Если архив предполагается распаковывать на других компьютерах, включая те, у которых меньше памяти, лучше выбирать размер словаря 128 МБ или меньше.
Поэтому firex, в принципе, прав...
 
Автор
joiner

joiner

Модератор
Локальный модератор
Сообщения
3,570
Репутация
632
я не спорю о правоте. просто пример нужен.
 

firex

AutoIT Гуру
Сообщения
943
Репутация
208
joiner
Ну если очень грубо, то примерно так:

Код:
#Include <WinAPI.au3>

Global Const $tagMYCOMPRESSEDPART = "dword Signature; uint64 PackLen;"
Global $tMYCOMPRESSEDPART = DllStructCreate( $tagMYCOMPRESSEDPART ), _
	$pMYCOMPRESSEDPART = DllStructGetPtr( $tMYCOMPRESSEDPART ), _
	$iMYCOMPRESSEDPART = DllStructGetSize( $tMYCOMPRESSEDPART )
	$tMYCOMPRESSEDPART.Signature = StringToBinary( "MYPK" )
; ---
Global Const $iPartSize = 1024 * 1024 * 1024, _
	$sFileName = "compress_me_pls.bin"

Global $hFile, $hResultFile, $bCompressed, $iCompressed_Size, $tPARTBODY, $iBytes
; *
$hFile = FileOpen( $sFileName )
$hResultFile = _WinAPI_CreateFile( $sFileName & ".compressed", 0 )

For $Idx = 1 To Ceiling( FileGetSize( $sFileName ) / $iPartSize ) Step 1
	$bCompressed = CompressMe( FileRead( $hFile, $iPartSize ) )
	$iCompressed_Size = BinaryLen( $bCompressed )

	$tMYCOMPRESSEDPART.PackLen = $iCompressed_Size
	$tPARTBODY = DllStructCreate( "byte Pack[" & $iCompressed_Size & "]" )
	$tPARTBODY.Pack = $bCompressed
	; -
	_WinAPI_WriteFile( $hResultFile, $pMYCOMPRESSEDPART, $iMYCOMPRESSEDPART, $iBytes )
	_WinAPI_WriteFile( $hResultFile, DllStructGetPtr( $tPARTBODY ), DllStructGetSize( $tPARTBODY ), $iBytes )
Next
_WinAPI_CloseHandle($hResultFile)
FileCLose( $hFile )


Аналогичным способом и декомпрессия ( наоборот ).
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
firex [?]
Аналогичным способом и декомпрессия ( наоборот ).
Декомпрессия как раз и вызывает вопросы. Так что не аналогично.


Добавлено:
Сообщение автоматически объединено:

Хотя, если в сжатом файле будет структура, в виде: длинна сжатых байт;сжатые байты, то да, возможно вы и правы.
 

firex

AutoIT Гуру
Сообщения
943
Репутация
208
inververs
-_-

Код:
#Include <WinAPI.au3>

Global Const $PK_SIGNATURE = StringToBinary( "MYPK" )
Global Const $tagMYCOMPRESSEDPART = "dword Signature; uint64 PackLen;"
Global $tMYCOMPRESSEDPART = DllStructCreate( $tagMYCOMPRESSEDPART ), _
	$pMYCOMPRESSEDPART = DllStructGetPtr( $tMYCOMPRESSEDPART ), _
	$iMYCOMPRESSEDPART = DllStructGetSize( $tMYCOMPRESSEDPART )

Global Const $sFileName = "compress_me_pls.bin.compressed"

Global $hFile, $hResultFile, $bDecompressed, $iCompressed_Size, $tPARTBODY, $iBytes
; *
$hFile = _WinAPI_CreateFile( $sFileName, 2, 2 )
$hResultFile = FileOpen( $sFileName & ".decompressed" )
While 1
	_WinAPI_ReadFile($hFile, $pMYCOMPRESSEDPART, $iMYCOMPRESSEDPART, $iBytes)
	If $tMYCOMPRESSEDPART.Signature <> $PK_SIGNATURE Then _
		ExitLoop

	$iCompressed_Size = $tMYCOMPRESSEDPART.PackLen
	; -
	$tPARTBODY = DllStructCreate( "byte Pack[" & $iCompressed_Size & "]" )
	_WinAPI_ReadFile( $hFile, DllStructGetPtr( $tPARTBODY ), DllStructGetSize( $tPARTBODY ), $iBytes )
	$bDecompressed = DecompressMe( $tPARTBODY.Pack )
	FileWrite( $hResultFile, $bDecompressed )
WEnd
FileClose( $hResultFile )
_WinAPI_CloseHandle($hFile)
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
firex, да просто не увидел сразу вот это "dword Signature; uint64 PackLen;"
 
Автор
joiner

joiner

Модератор
Локальный модератор
Сообщения
3,570
Репутация
632
твой способ заклинил систему :smile:
в системе 4 гига. последний объем памяти перед зависанием - около 1.5 гб

вот как делаю я. в комментариях то, чего нужно достичь
Код:
;упаковка частями. к примеру, файл большой
$for = FileOpen('test.jpg',16)
$fow = FileOpen('test.bin',18)
While 1
	$frs = FileRead($for,4096)
	If @error = -1 Then ExitLoop
	$cb = _LZNTCompress($frs)
	FileWrite($fow,$cb)
WEnd
FileClose($for)
FileClose($fow)

;распаковка. считываем файл полностью. на выходе получаем читаемый исходный файл
;но нужно частями, если файл большого размера
$frb = FileRead('test.bin')
$db = _LZNTDecompress($frb)
FileWrite('test_new.jpg',$db)

функции взяты из вложения в первом сообщении. пример полностью рабочий
 

firex

AutoIT Гуру
Сообщения
943
Репутация
208
joiner
Подобные операции последний раз проводил на 3.3.8.1, может в последней версии появились проблемы с освобождением памяти?
Попробуй замени:
Код:
$tPARTBODY = DllStructCreate( "byte Pack[" & $iCompressed_Size & "]" )


На :
Код:
$tPARTBODY = 0
$tPARTBODY = DllStructCreate( "byte Pack[" & $iCompressed_Size & "]" )
 
Автор
joiner

joiner

Модератор
Локальный модератор
Сообщения
3,570
Репутация
632
firex
код декомпрессии не работает. честно сказать, не пойму как он может работать
Код:
$hFile = _WinAPI_CreateFile( $sFileName, 2 )

по умолчанию открывает файл для записи
а ты его начинаешь читать в цикле
 

firex

AutoIT Гуру
Сообщения
943
Репутация
208
joiner
Так я не проверял, а только черканул шаблон для разъяснения. Сейчас поправлю.
 

Garrett

Модератор
Локальный модератор
Сообщения
3,999
Репутация
967
joiner [?]
считывать файл частями и проводить декомпрессию - значит получить на выходе нечитаемый файл.
Почему?
IMHO RtlDecompressBufferEx поможет.
WorkSpace [in]
A pointer to a caller-allocated work space buffer used by the RtlDecompressBufferEx function during decompression. Use the RtlGetCompressionWorkSpaceSize function to determine the correct work space buffer size.
RtlGetCompressionWorkSpaceSize
CompressFragmentWorkSpaceSize [out]
A pointer to a caller-allocated buffer receiving the size, in bytes, required to decompress a compressed buffer to a fragment. This value is used to determine the correct size of RtlDecompressFragment's WorkSpace buffer. Note that the RtlCompressFragment function does not currently exist.

Также можно обратить внимание на функции CreateFileMapping и MapViewOfFileEx.

P.S. Если я правильно понял, декомпрессия файла фрагментами.
 
Автор
joiner

joiner

Модератор
Локальный модератор
Сообщения
3,570
Репутация
632
я пришел к тому, с чего и начинал - нужно считывать размер сжатых данных.
сжимаем файл и в него дописываем структуру распаковки - какими порциями считывать
в примере использован общий массив для наглядности
Код:
$for = FileOpen('test.rar', 16)
$fow = FileOpen('test.bin', 18)
Local $array[0], $i = 0
While 1
	$frs = FileRead($for, 1048576)
	If @error = -1 Then ExitLoop
	$cb = _LZNTCompress($frs)
	FileWrite($fow, $cb)
	ReDim $array[$i + 1]
	$array[$i] = BinaryLen($cb)
	$i += 1
WEnd
FileClose($for)
FileClose($fow)

$foc = FileOpen('test.bin', 16)
$fod = FileOpen('test_new.rar', 18)
Local $i = 0
While 1
	If $i = UBound($array) Then ExitLoop
	$frb = FileRead($foc, $array[$i])
	$db = _LZNTDecompress($frb)
	FileWrite($fod, $db)
	$i += 1
WEnd
FileClose($foc)
FileClose($fod)
все архиваторы не спрашивают как сжат файл. видимо, они читают эти данные из файла(архива) и потом читают и распаковывают целиком или фрагментами
 
Верх