Что нового

Удаление файлов из архива *.zip

RUVATA

лучше один раз увидеть, чем десять раз услышать...
Сообщения
132
Репутация
38
Всем доброго времени суток...
Стоит задача:
разобраться и по возможности оптимизировать довольно большое количество архивов в формате zip... (Архиватор 7-zip)

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

Все это исполнено на AutoIt...
И так хочется, решить еще одну задачку не прибегая к использованию других ЯП и технологий, а именно:
В большом количестве архивов, часть файлов является ненужными или повторяющимися от архива к архиву без изменений. Мои предшественники по этому поводу не заморачивались, т.к. файлы эти в упакованном состоянии - довольно "легкие", по 1,5 - 2 Мб.
Автоматизировать процесс им видимо не хватало мозгов, а выполнять вручную непозволяла вселенская лень.
В масштабе одного архива это конечно было незаметно... Да и изначально их задумывалось делать 2-3 раза в месяц...
но ситуация изменилась и потом их стали лепить ежедневно, а потом и по 2-3 раза в день. Итого у меня сейчас ~4500 шт. архивов в каждом из которых 1,5 - 2 Мб мусора, что есть ~ 8 Гб.
Обидно и непрофессионально, - хочу исправить...

Я уже имел дело с фреймворками для .NET которые позволяют работать с zip как есть без перепаковки.
Т.е. я как бы могу сейчас вооружиться Visual Studio и на C# накидать что-то подходящее...
НО Я ХОЧУ НА AutoIt !!! (катаясь по полу, стуча кулаками)

Вручную при помощи архиватора 7-zip (обожая уважаю 8) ) так-же можно редактировать содержание архива... но среди функционала консольной версии 7za я пока такого трюка не нашел (хотя поиски продолжаются)

Обращаясь к великому коллективному сознанию, хочу спросить кто, что по этому поводу знает, думает, предполагает и т.д.
 

ynbIpb

Скриптер
Сообщения
399
Репутация
110
пример содержимого архива и какие файлы надо удалять в студию!
удалять рекурсивно? тоесть перебирать все каталоги и подкаталоги? Имена файлов одинаковые или разные?

з.ы.
топик больше для раздела Стол заказов
 
Автор
R

RUVATA

лучше один раз увидеть, чем десять раз услышать...
Сообщения
132
Репутация
38
ynbIpb [?]
топик больше для раздела Стол заказов
Дык я как бы для развития функционала своего приложения, мне готовенького "нэ требо", мне сопособ понять.

Рекусивно.
Например в архиве полезные файлы имеют расширение *.CD чаще всего такой файл один, но не всегда. Все остальное - можно kill (В отдаленном принципе :smile: пусть в процессе вкуривания пока будет так)

Мне бы глазком поглядеть как это делается...

PS: Уже накопал Zip_UDF, но пока еще не углублялся...
пока курю 7za, все прелестно за исключением одного - содержимое архива кажет таблицей в MS-DOS(cp866)... Распарсить с ходу не получается...
вобщем вот так (см.вложение)
далее все тот-же 7za позволяет delete по полному путь+имя (относительно корня архива)
 

Garrett

Модератор
Локальный модератор
Сообщения
3,999
Репутация
964
RUVATA [?]
содержимое архива кажет таблицей в MS-DOS(cp866)... Распарсить с ходу не получается...
Код:
#include <Encoding.au3>
_Encoding_866To1251($sData)



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

Код:
Listing archive: Doc.zip

--
Path = Doc.zip
Type = zip
Physical Size = 133

   Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2011-05-10 14:29:32 ....A           11           11  Doc_Тест.txt
------------------- ----- ------------ ------------  ------------------------
                                    11           11  1 files, 0 folders
 

ynbIpb

Скриптер
Сообщения
399
Репутация
110
Если нужны только *.CD файлы (я их не заметил в списке, может *.1CD), то проще создавать новые архивы чем ковыряться в старых.
Распаковываешь в темп, все файлы с этим расширением, создаёшь архив с именем старого и заменяешь.
Писал я нечто похожее на заказ, использовал консольный 7z.exe (он ссобой Длл'ку таскает)
Информация из справки 7зипа:
7z e archive.zip -oc:\soft *.1cd -r
extracts all *.1cd files from archive archive.zip to c:\soft folder.
и нечего заморачиваться с кодировками
Собирай все имена архивов в массив _FileListToArray и потом в цикле For Next перебирай.
Код:
#Include <File.au3>
#Include <Array.au3>
$s7z_exe = @ScriptDir & "\data\7z.exe" ; путь к консольной версии архиватора 7zip
$s7z_dll = @ScriptDir & "\data\7z.dll" ; путь к библиотеке, которую использует 7zip

_unpack ("D:\архивы\111111.zip")

Func _unpack ($sArch)
	$sTempDir = @TempDir & "\Arch_unpacker" ; временная папка, куда будут извлекаться файлы
	If FileExists ($sArch) = 0 Then Return "" ; если архива нет, то возвращаемся из функции
	RunWait ( $s7z_exe & ' e ' & '"' & $sArch & '" -o"'& $sTempDir &'" *.1CD -r -aoa', @ScriptDir & '\data', @SW_HIDE); запускаем архиватор и приостанавливаем скрипт
	$aTempFiles = _FileListToArray($sTempDir, "*.*", 1) ; собираем имена всех файлов в этом  временном каталоге
	_ArrayDisplay($aTempFiles) ; отладка
	DirRemove ($sTempDir, 1) ; после обработки удаляем все временные файлы вместе с папкой.
EndFunc
 
Автор
R

RUVATA

лучше один раз увидеть, чем десять раз услышать...
Сообщения
132
Репутация
38
ynbIpb [?]
я их не заметил в списке, может *.1CD
верно, верно - опечаточка... *.1CD (это файлы БД 1С 8.х)

Распаковываешь в темп, все файлы с этим расширением, создаёшь архив с именем старого и заменяешь.
очень ресурсоемко... учитывая, что ненужные файлы имеют 2-6 % от общего объема
берем один из моих архивов весом в 650 Мб - вот результаты по скорости:
Если заранее знать что удалять то 7za.exe (облегченная консольная версия 7zip) ей можно указать список в *.txt - удаляет все лишнее за 1,2 мин.
А вот только на распаковку *.1CD из архива необходимо 3,6 мин. + 1,4 Гб темпа... Дальнейшее пересжатие такой-же степени (Ultra) занимает 6,2 мин, потом удаление первоначалного архива (с мусором) еще 40 сек.
итого 10,2 мин.
Я считаю неоправданно, при учете что все таки удастся парсить содержимое архивов налету и подготавливать список для 7za.exe

Garrett, Спасибо... так действительно можно захватив вывод 7za.exe в переменную, парсить ее содержимое налету (не сохраняя в файл)... пригодится...
 

Garrett

Модератор
Локальный модератор
Сообщения
3,999
Репутация
964
RUVATA
В файле справки 7z обратите внимание на Switches -u мне кажется это то, что вам нужно (правда там есть некоторые ограничения, но всё же).
 
Автор
R

RUVATA

лучше один раз увидеть, чем десять раз услышать...
Сообщения
132
Репутация
38
Garrett
более полезен Switches -x

7za.exe d test.zip -r -x!*.1CD при таких параметрах
за молниеносно быстро рекурсивно удаляются все файлы кроме *.1СD... причем -x! можно задавать сколь угодно много...
Завтра опишу на AutoIt и выложу мой нехитрый миханизм:
Разбирает результаты list по архиву... на основании его составляет список, и подает его на завтрак вместе с параметром delete
 
Автор
R

RUVATA

лучше один раз увидеть, чем десять раз услышать...
Сообщения
132
Репутация
38
Уважаемые посоветуйте...
Задача рекурсивно обойти каталог (вложеность глубиной 5-6)...
каталог забит архивами *.zip, иных файлов в нем нет... Задача все это дело обойти и для каждого архива выполнить нашу "зачистку"...
Вопрос №1 - делать все это одим скриптом циклически или функцией в цикле, или саму процедуру "чистки" реализовать отдельным скриптом (при этом опять-же загружен интерпритатор), а есть еще идея эту вот самую "чистку" скомпилить и а из скрипта вызывать (так помоему максимально эфективно, под каждый будет создаваться свой процесс) но тогда у меня попутный вопрос как к скомпилированным скриптам реализуется передача параметров
Вопрос №2 - Правильнее будет сначала рекурсивно обойти и забить н\п массив - а потом подставляя его значения обрабатывать... или находу т.е. нашел - запустил, следующий...
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,320
RUVATA
ИМХО, можно примерно так попробовать:
Код:
$sDir = 'Полный путь к папке'

$aResult = _FileSearch($sDir, '*.zip', 1)
If @error Then
	MsgBox(16, 'Error', 'Error')
	Exit
EndIf
If Not $aResult[0] Then
	MsgBox(64, 'Info', 'Нет файлов *.zip')
	Exit
EndIf
For $i = 1 To $aResult[0]
	; здесь обрабатываете архивы
Next

Func _FileSearch($s_Path_Search, $s_File_Mask = '*', $i_Flag = 0, $i_SubDir = 1)
	;фильтры для поиска нужно указывать через ;
	;$i_Flag = 0 - Файлы и папки (по умолчанию)
	;$i_Flag = 1 - Только файлы
	;$i_Flag = 2 - Только папки
	;$i_SubDir = 1 - Искать во всех подкаталогах (по умолчанию). Возвращает полные пути.
	;$i_SubDir = 0 - Искать только в самой папке. Возвращает только имена файлов(папок).
	Local $s_Out, $a_Out, $s_Read, $h_Dir, $s_Attrib, $s_Subdir, $a_Masks

	If StringRight($s_Path_Search, 1) == '\' Then ;нужно, если $s_Path_Search - диск (например C:\)
		$s_Path_Search = StringTrimRight($s_Path_Search, 1)
	EndIf
	If $i_SubDir Then
		$s_Subdir = ' /S /B'
	Else
		$s_Subdir = ' /B'
	EndIf
	Switch $i_Flag
		Case 1
			$s_Attrib = ' /A-D'
		Case 2
			$s_Attrib = ' /AD'
		Case Else
			$s_Attrib = ' /A'
	EndSwitch
	$s_Out = StringToBinary('0' & @CRLF, 2)
	$a_Masks = StringSplit($s_File_Mask, ';')
	For $i = 1 To $a_Masks[0]
		$h_Dir = Run(@ComSpec & ' /U /C DIR "' & $s_Path_Search & '\' & $a_Masks[$i] & '"' & $s_Subdir & $s_Attrib, @SystemDir, @SW_HIDE, 6)
		If Not $h_Dir Then Return SetError(1)
		While 1
			$s_Read = StdoutRead($h_Dir, False, True)
			If @error Then
				ExitLoop
			EndIf
			If $s_Read <> "" Then
				$s_Out &= $s_Read
			EndIf
			Sleep(10)
		WEnd
	Next

	$a_Out = StringRegExp(BinaryToString($s_Out, 2), '[^\r\n]+', 3)
	If @error Then Return SetError(1)
	$a_Out[0] = UBound($a_Out) - 1
	Return $a_Out
EndFunc   ;==>_FileSearch
 
Автор
R

RUVATA

лучше один раз увидеть, чем десять раз услышать...
Сообщения
132
Репутация
38
madmasles
спасибо... пока что раскуриваю функцию _FileSearch ...
... честно думал что для рекурсивного обхода, есть встроенные средства... уложил даже парочку закладок но еще не читал...
Но оч. интересно покопать "внутренности"
пытаюсь понять черную магию вот этого
Код:
$h_Dir = Run(@ComSpec & ' /U /C DIR "' & $s_Path_Search & '\' & $a_Masks[$i] & '"' & $s_Subdir & $s_Attrib, @SystemDir, @SW_HIDE, 6)
 
Автор
R

RUVATA

лучше один раз увидеть, чем десять раз услышать...
Сообщения
132
Репутация
38
О боги... мне как-то было втяготы понять что мы юзаем CMD ( от которого бежал как от огня в AutoIT :'( )
:shok: поплохело вдруг...
Надо поискать другой путь (просто чтобы убедиться что я обречен) :'(
 
Автор
R

RUVATA

лучше один раз увидеть, чем десять раз услышать...
Сообщения
132
Репутация
38
Вот такие инструменты у меня получились... с их помощью я достиг своей цели... и еще не раз наверное использую эти наработки...
Суть
Далле в коде описаны Две функции:
I. Struct_Reader([$TagertDir],[$OldDir]) -
$TagertDir - путь к целевому каталогу, поумолчанию = @ScriptDir
$OldDir - служебный параметр, если изучите код поймете зачем :smile:

Эта функция принемает первым параметром адрес изучаемого каталога, и возвращает массив массивов (обзовем его "супермассив" :IL_AutoIt_1: ) который описывает его структуру ;
Супермассив двухмерный, и все его подмассивы тоже,
в первой размерности имена (файлов/каталогов), а во второй:
1) для каталогов - содержит массив аналогичным образом описывающий его структуру и т.д.
2) для файлов - метка "file" (в идеале я думал о том, чтобы туда писать какую ни будь полезную инфу, но пока так)
PS: За нулевым индексом каждого из массивов закреплена "метаинформация" а именно в первой размерности полный путь к описываемому каталогу, во второй размерности специальные маркеры "no_files" или "no_dirs" или пусто(Nothing) если в каталог есть и те и другие.

II. Struct_Finder($TagertArray, [$TagertDir])
$TagertArray - наш супермассив :smile: возвращенный функцией Struct_Reader ; обработку подсовывания всякого "Г" я не делал... так что :whistle:
$TagertDir - поддиректория содержимое которой необходимо получить, по умолчанию = @ScriptDir

Эта функция возвращает срез массива массивов для указанной поддиректории

PS: Возвращаемый массив - является массивом одного из нижних порядков, но так-же как и родительский - супермассив, с определенным уровнем вложенности (если не описывает последний уровень вложенности).

Код:
#Include <File.au3>
#Include <Array.au3>

;Демонстрация
$Demonstration = Struct_Reader() ; Если исследуемый каталог не каталог скрипта, можно указать путь.
Struct_Finder($Demonstration) ; Вторым параметром этой функции можно задать конкретный подкаталог
;Демонстрация

Func Struct_Reader($TagertDir = @ScriptDir, $OldDir = "") 
Dim $Marker
If $TagertDir <> @ScriptDir Then $TagertDir = $OldDir & "\" & $TagertDir
	
$Dirs=_FileListToArray($TagertDir, "*", 2) ; Определяем вложенные каталоги
	If (not IsArray($Dirs)) Then ; Если вложенных каталогов нет, объявляем пустой массив $Dirs
		Dim $Dirs[1]
		$Marker = "D"
	EndIf

$Files=_FileListToArray($TagertDir, "*", 1) ; Определяем вложенные файлы
	If (not IsArray($Files)) Then ; Если вложенных файлов нет, объявляем пустой массив $Files
		Dim $Files[1]
		$Marker = "F"
	EndIf
				
If Ubound($Dirs) + Ubound($Files) > 0 Then ; Подводим итоги предшествующих проверок, - стуктурируем массив
	Dim $Structure[$Dirs[0] + $Files[0] + 1][2]
	$Structure[0][0] = $TagertDir ; Сохраняем путь к опрошенному каталогу.	
		If $Marker = "D" Then $Structure[0][1] = "no_dirs"  ; Если в дирректории необнаруженно либо файлов либо каталогов 
		If $Marker = "F" Then $Structure[0][1] = "no_files" ; Оставляем "маркер" мне нужно было для отладки, хотя наверное можно найти и практическое прменение :)
			
		If $Marker <> "D" Then ; Если подиректории есть то опишем их, и отметим как "dir" для избиратеольной рекурсии
			For $i = 1 to UBound($Dirs) - 1	
					$Structure[$i][0] = $Dirs[$i]
					$Structure[$i][1] = "dir"
			Next
		Else
			$i = 1	
		EndIf 
		
		If $Marker <> "F" Then ; Если есть вложенные файлы - добавляем их к концу масиива, отмечаем как "File"
							   ; Это мой личный "бзык" так папки будут вверху файлы внизу//
			For $z = $i to $i + $Files[0] - 1 
					$Structure[$z][0] = $Files[$z - $i + 1]
					$Structure[$z][1] = "file" 
			Next	
		Endif
EndIf

For $i = 1 to Ubound($Structure) - 1    ; Собственно рекурсия функции
	If $Structure[$i][1] = "dir" Then 
		$Structure[$i][1] = Struct_Reader($Structure[$i][0], $Structure[0][0]) 
	EndIf
Next

Return $Structure

EndFunc

Func Struct_Finder ($TagertArray, $TagertDir = @ScriptDir)

If StringRight($TagertDir, 1) = "\" Then $TagertDir = StringTrimRight($TagertDir, 1)

If $TagertArray[0][0] <> $TagertDir Then
		For $i = 1 to UBound($TagertArray)-1
		If IsArray($TagertArray[$i][1]) Then Struct_Finder($TagertArray[$i][1], $TagertDir)
		Next
Else
	$Result = $TagertArray
EndIf

_ArrayDisplay($Result, "Result")
EndFunc
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,716
Я не читал всю эту ветку, но по-моему, это проще сделать с помощью 7Zip.au3. Данный пример выводит информацию о содержимом всех .zip и .7z архивов в указанной папке и всех ее подпапках (обычная рекурсия). _FileListToArrayEx() - это просто улучшенный вариант _FileListToArray().

Код:
#Include <7Zip.au3>
#Include <Date.au3>

_FindFiles(@DesktopDir & '\Test', '*.zip;*.7z')

Func _FindFiles($sRoot, $sFile, $sFilter = '*')

	Local $FL

	$FL = _FileListToArrayEx($sRoot, $sFile, 1)
	If Not @error Then
		For $i = 1 To $FL[0]
			_EnumArcFiles($sRoot & '\' & $FL[$i], $sFilter)
		Next
	EndIf
	$FL = _FileListToArrayEx($sRoot, '*', 2)
	If Not @error Then
		For $i = 1 To $FL[0]
			_FindFiles($sRoot & '\' & $FL[$i], $sFile)
		Next
	EndIf
EndFunc   ;==>_FindFiles

Func _EnumArcFiles($sPath, $sFilter)

	ConsoleWrite($sPath & @CR)
	ConsoleWrite('------------------------------------------------------------' & @CR)

	Local $hArc, $tInfo, $Result = 0

	Do
		$hArc = _7ZipOpenArchive(0, $sPath)
		If Not $hArc Then
			ExitLoop
		EndIf
		$tInfo = _7ZipFindFirst($hArc, $sFilter)
		If Not IsDllStruct($tInfo) Then
			ExitLoop
		EndIf
		Do
			$File = DllStructGetData($tInfo, 'szFileName')
			If StringRight($File, 1) <> '\' Then
				ConsoleWrite('File:   ' & $File & @CR)
				ConsoleWrite('Size:   ' & DllStructGetData($tInfo, 'dwOriginalSize') & @CR)
				ConsoleWrite('Date:   ' & _Date_Time_DOSDateToStr(DllStructGetData($tInfo, 'wDate')) & @CR)
				ConsoleWrite('Time:   ' & _Date_Time_DOSTimeToStr(DllStructGetData($tInfo, 'wTime')) & @CR)
				ConsoleWrite('Attrib: ' & DllStructGetData($tInfo, 'szAttribute') & @CR)
				ConsoleWrite(@CR)
			EndIf
			$tInfo = _7ZipFindNext($hArc, $tInfo)
		Until Not IsDllStruct($tInfo)
		$Result = 1
	Until 1
	If Not $Result Then
		ConsoleWrite('Error!' & @CR)
	Else
		ConsoleWrite(@CR)
	EndIf
	Return $Result
EndFunc   ;==>_EnumArcFiles

Func _FileListToArrayEx($sPath, $sFilter = "*", $iFlag = 0)
    Local $hSearch, $aResult, $sFile, $sFileList, $sDelim = "|"
    $sPath = StringRegExpReplace($sPath, "[\\/]+\z", "") & "\"
    If Not FileExists($sPath) Then Return SetError(1, 1, "")
    If StringRegExp($sFilter, "[\\/:><\|]|(?s)\A\s*\z") Then Return SetError(2, 2, "")
    If Not ($iFlag = 0 Or $iFlag = 1 Or $iFlag = 2) Then Return SetError(3, 3, "")
    $hSearch = FileFindFirstFile($sPath & '*')
    If @error Then Return SetError(4, 4, "")
    While 1
        $sFile = FileFindNextFile($hSearch)
        If @error Then ExitLoop
        If ($iFlag + @extended = 2) Then ContinueLoop
        $aResult = DllCall('shlwapi.dll', 'int', 'PathMatchSpecW', 'wstr', $sFile, 'wstr', $sFilter)
        If (Not @error) And ($aResult[0]) Then $sFileList &= $sDelim & $sFile
    WEnd
    FileClose($hSearch)
    If Not $sFileList Then Return SetError(4, 4, "")
    Return StringSplit(StringTrimLeft($sFileList, 1), "|")
EndFunc   ;==>_FileListToArrayEx


EnumArchive.zip

P.S

Удалить файл из архива можно с помощью функции _7ZipDelete() из того же 7Zip.au3.
 
Автор
R

RUVATA

лучше один раз увидеть, чем десять раз услышать...
Сообщения
132
Репутация
38
Yashied
Будет время обязательно попробую... но я решил справиться сам...
при помощи своей наработки я получил структуру в супермассив (все завязано на рекурсию это само сабой :smile: )
А далее дело техники -
7za.exe d test.zip -r -x!*.1CD при таких параметракто молниеносно быстро рекурсивно удаляются все файлы кроме *.1СD... причем -x! можно задавать сколь угодно много...
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,716
"Супермассивы" - это запутанный код + сложное использование + ограничение + нежелательное использование мссива в массиве.
 
Автор
R

RUVATA

лучше один раз увидеть, чем десять раз услышать...
Сообщения
132
Репутация
38
Yashied [?]

"Супермассивы" - это запутанный код + сложное использование + ограничение +
Код не слишком запутанный, на мой взгляд... если например сравнить с приведенным вами кодом _FileListToArray()
Разобраться можно, пусть не с ходу как книгу но можно... причем я позаимствовал конструкцию из C++, мой супермассив не что иное как колекция, просто AutoIt добавляет шарма, т.к. не объектноориентированый :IL_AutoIt_1:

нежелательное использование массива в массиве.
- это верно... в данном случае это факт, но иначе не выходит - динамическая типизация, невозможность определять классы и т.д.

А вот насчет ограничений и сложного использования - можно по подробнее ?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,716
Код:
$Demonstration = Struct_Reader('C:\')


Код:
C:\Users\Jack\Desktop\New folder\New AutoIt v3 Script.au3 (71) : ==> Variable used without being declared.:
_ArrayDisplay($Result, "Result")
_ArrayDisplay(^ ERROR
 
Верх