Имя пользователя:
Пароль:  
Помощь | Регистрация | Забыли пароль?  | Правила  

Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » AutoIt » Скрипт для разбиения файла на части

Ответить
Настройки темы
Скрипт для разбиения файла на части

Пользователь


Сообщения: 135
Благодарности: 32

Профиль | Отправить PM | Цитировать


Изменения
Автор: Diamond
Дата: 31-08-2009
Описание: Изменения в коде пакетного файла
Скрипт позволяет разбивать файл на части, которые в последствии можно объеденить с помощью пакетного
файла без нарушения внутреннего форматирования (см. команда Copy /?).
После разбиения, скрипт создаёт пакетный файл "Combine all parts.bat" в том же директории куда копируются части.
P.S. Надеюсь это кому нибудь пригодится.
читать дальше »
Код: Выделить весь код
#include <WinAPI.au3>
#include <GuiConstants.au3>
#include <WindowsConstants.au3>
#include <GuiStatusBar.au3>
Global $Title = "SplitFile", $Msg, $ret, $fSplitRunning
Global $Gui = GUICreate($Title, 500, 240)
GUICtrlCreateLabel("Путь к файлу:", 5, 10, 104, 22)
GUICtrlCreateLabel("Сохранить в:", 5, 50, 104, 22)
GUICtrlCreateLabel("Размер части:", 5, 90, 104, 22)
Global $Input1 = GUICtrlCreateInput("",110, 10, 300, 22, 0x0080)
    GUICtrlSetTip(-1, "Путь к файлу-источнику.")
Global $Input2 = GUICtrlCreateInput("C:\Split",110, 50, 300, 22, 0x0080)
    GUICtrlSetTip(-1, "Директорий для сохранения частей.")
Global $Input3 = GUICtrlCreateInput("", 110, 90, 300, 22, 0x2000+0x0080)
    GUICtrlSetTip($Input3, "Размер каждой части в Мегабайтах.")
Global $Button1 = GUICtrlCreateButton("Обзор", 420, 10, 70, 22)
Global $Button2 = GUICtrlCreateButton("Обзор", 420, 50, 70, 22)
Global $Button3 = GUICtrlCreateButton("Разделить", 420, 140, 70, 40)
For $i = $Input1 To $Input3
    GUICtrlSetFont($i-3, 11.5, 800, 0, "Times New Roman")
    GUICtrlSetFont($i, 11.5, 500, 0, "Arial")
Next
Global $radio1 = GUICtrlCreateRadio("Bytes", 10, 140, 50, 20)
Global $radio2 = GUICtrlCreateRadio("KBytes", 10, 160, 60, 20)
Global $radio3 = GUICtrlCreateRadio("MBytes", 10, 180, 60, 20)
    GUICtrlSetState($radio3, $GUI_CHECKED)
Global $Progress = GUICtrlCreateProgress(0, 0, -1, -1, 0x01)
Global $hProgress = GUICtrlGetHandle($Progress)
Global $aParts[4] = [1, 222, 270, -1]
Global $hStatusBar = _GUICtrlStatusBar_Create($Gui, $aParts), $fStatusBarSpoiled = False
_GUICtrlStatusBar_EmbedControl($hStatusBar, 1, $hProgress, 4)
_GUICtrlStatusBar_SetText($hStatusBar, "0%", 2)

GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")
GUIRegisterMsg($WM_SYSCOMMAND, "WM_SYSCOMMAND")
GUIRegisterMsg($WM_PAINT, "WM_PAINT")
GUISetState()

While 1
    $msg = GUIGetMsg()
    Switch $msg
    Case $GUI_EVENT_CLOSE
        Exit
    Case $Button1
        GUISetState(@SW_DISABLE, $Gui)
        $ret = FileOpenDialog($Title, "", 'Все файлы (*.*)', 1, "", $Gui)
        If Not @error Then GUICtrlSetData($Input1, $ret)
        GUISetState(@SW_ENABLE, $Gui)
    Case $Button2
        GUISetState(@SW_DISABLE, $Gui)
        $ret = FileSelectFolder("Выберите пустой директорий для сохранения частей.", "", 1, "", $Gui)
        If Not @error And FileExists($ret) Then GUICtrlSetData($Input2, $ret)
        GUISetState(@SW_ENABLE, $Gui)
    Case $Button3
        If Split() = -1 Then $fSplitRunning = False
    Case $radio1
        GUICtrlSetTip($Input3, "Размер каждой части в Байтах.")
    Case $radio2
        GUICtrlSetTip($Input3, "Размер каждой части в Килобайтах.")
    Case $radio3
        GUICtrlSetTip($Input3, "Размер каждой части в Мегабайтах.")
    EndSwitch
WEnd

Func Split()
    $fSplitRunning = True ; Флаг $fSplitRunning сообщает о том что функция Split была запущена или завершена
    Local $ret, $sFile, $sDir, $iSplitSize, $aDirInfo, $iSize, $i, $PartCount, $Current, $newState, $oldState
    Local $iBytes, $tBuffer, $hFileOpen, $hFileWrite, $sExt, $sFileName, $sBat
    If @ScriptDir <> @WorkingDir Then FileChangeDir(@ScriptDir)
    $sFile = GUICtrlRead($Input1)
    $sDir = GUICtrlRead($Input2)
    For $i = $radio1 To $radio3
        If GUICtrlRead($i) = $GUI_CHECKED Then ExitLoop
    Next
    $iSplitSize = GUICtrlRead($Input3)*1024^($i-$radio1)
    If Not FileExists($sFile) Then
        _MsgBox(16, $Title, "Файл не существует.", $Gui)
        Return -1
    EndIf
    If Not FileExists($sDir) Then
        If Not DirCreate($sDir) Then
            _MsgBox(16, $Title, "Укажите путь к папке.", $Gui)
            Return -1
        EndIf
    Else
        $aDirInfo = DirGetSize($sDir, 1)
        If $aDirInfo[1]+$aDirInfo[2] > 0 Then
        $ret = _MsgBox(16, $Title, "Указанная папка не пуста.", $Gui)
        Return -1
    EndIf
    EndIf
    If DriveGetType($sDir) <> "Fixed" Then
        _MsgBox(16, $Title, "Указанный тип носителя не поддерживается.", $Gui)
        Return -1
    EndIf
    If Not $iSplitSize Then
        _MsgBox(16, $Title, "Установлено недопустимое значение размера для части.", $Gui)
        Return -1
    EndIf
    $iSize = FileGetSize($sFile)
    If $iSplitSize >= $iSize Then
        _MsgBox(16, $Title, "Указанный размер, превышает или равен размеру файла.", $Gui)
        Return - 1
    EndIf
    ;===============================================
    $PartCount = Ceiling($iSize/$iSplitSize)
    $sDir = StringRegExpReplace($sDir,"\\$","") & "\"
    $sFileName = StringRegExpReplace($sFile,"^.*\\","")
    $sExt = "." & StringRegExpReplace($sFileName, "^.*\.|^.*","")
    _GUICtrlStatusBar_SetText($hStatusBar, "Открытие файла...", 3)
    $hFileOpen = _WinAPI_CreateFile($sFile, 2, 2)
    $Current = $iSplitSize
    _GUICtrlStatusBar_SetText($hStatusBar, "Разделение...", 3)
    For $i = 1 To $PartCount
        If $i = $PartCount Then $Current = $iSize-$iSplitSize*($i-1)
        $tBuffer = DllStructCreate("byte[" & $Current & "]")
        _WinAPI_ReadFile($hFileOpen, DllStructGetPtr($tBuffer), $Current, $iBytes)
        $hFileWrite = _WinAPI_CreateFile($sDir & "part" & $i & $sExt, 1, 4)
        _WinAPI_WriteFile($hFileWrite, DllStructGetPtr($tBuffer), $Current, $iBytes)
        _WinAPI_CloseHandle($hFileWrite)
        _WinAPI_SetFilePointer($hFileOpen, 0, 1) ; смещение от текущей позиции на указанный размер($Current)
        $newState = Round(100*$i/$PartCount)
        If $newState <> $oldState Then
            GUICtrlSetData($Progress, $newState)
            _GUICtrlStatusBar_SetText($hStatusBar, $newState & "%", 2)
            $oldState = $newState
        EndIf
    Next
    _WinAPI_CloseHandle($hFileOpen)
    $sBat = StrBatCreate($sExt, $sFileName, $PartCount)
    FileWrite($sDir & "Combine all parts.bat", $sBat)
    GUICtrlSetData($Progress, $newState)
    _GUICtrlStatusBar_SetText($hStatusBar, "100%", 2)
    Sleep(400)
    GUICtrlSetData($Progress, 0)
    _GUICtrlStatusBar_SetText($hStatusBar, "0%", 2)
    _GUICtrlStatusBar_SetText($hStatusBar, "Готово", 3)
    Return -1
EndFunc

Func StrBatCreate($sExt, $sDestFile, $iCount)
    Local $sBat
    $sBat = '@echo off' & @LF & _
    'title Подготовка...' & @LF & _
    'setlocal enableextensions enabledelayedexpansion' & @LF & _
    'set strTempFolder=$temp%random%' & @LF & _
    'md ".\%strTempFolder%"' & @LF & _
    'for /l %%i in (1,1,' & $iCount & ') do (set strPartFile=Part%%i'& $sExt & @LF & _
    'if exist "!strPartFile!" (move "!strPartFile!" ".\%strTempFolder%\!strPartFile!">nul))' & @LF & _
    'title Слияние...' & @LF & _
    'copy /b ".\%strTempFolder%\Part*'& $sExt & '" "' & $sDestFile & '"' & @LF & _
    'title Пожалуйста, дождитесь завершения...' & @LF & _
    'move ".\%strTempFolder%\Part*'& $sExt & '" ".">nul' & @LF & _
    'rd /s /q ".\%strTempFolder%"'
    Return CharToOem($sBat)
EndFunc

Func WM_COMMAND($HWnd, $MsgID, $wParam, $lParam)
    Local $iFile, $CtrlID, $iSplitSize, $i, $iPartCount, $iFileSize
    $CtrlID  = BitAND($wParam, 0xFFFF)
    Switch $CtrlID
        Case $Button1 To $Button3
        If $fSplitRunning Then
            DllCall("User32.dll", "int", "MessageBeep", "uint", 16)
            Return 1
        EndIf
        Case $Input1, $Input3, $radio1 To $radio3
        $iFile = GUICtrlRead($Input1)
        If FileExists($iFile) And Not $fSplitRunning Then
            For $i = $radio1 To $radio3
                If GUICtrlRead($i) = $GUI_CHECKED Then ExitLoop
            Next
            $iSplitSize = Number(GUICtrlRead($Input3)*1024^($i-$radio1))
            $iFileSize = FileGetSize($iFile)
            $iPartCount = Ceiling($iFileSize/$iSplitSize)
            If $iPartCount < 0 Or $iFileSize <= $iSplitSize Then $iPartCount = 0
            _GUICtrlStatusBar_SetText($hStatusBar, "Кол-во частей: " & $iPartCount, 3)
        EndIf
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc

Func WM_PAINT($HWnd, $MsgID)
    If $fStatusBarSpoiled Then ; "перерисовка" StatusBar
        _GUICtrlStatusBar_EmbedControl($hStatusBar, 1, $hProgress, 4)
        $fStatusBarSpoiled = False
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc

Func WM_SYSCOMMAND($HWnd, $MsgID, $wParam, $lParam)
    Local Const $SC_RESTORE = 0xF120
    ;Устанавливаю флаг $fStatusBarSpoiled в True (для WM_PAINT это обозначит что StatusBar был неверно отрисован при разворачивании окна.)
    If $wParam = $SC_RESTORE Then $fStatusBarSpoiled = True
    Return $GUI_RUNDEFMSG
EndFunc

Func _MsgBox($MsgBoxType, $MsgBoxTitle, $MsgBoxText, $Main_GUI)
    Local $ret, $err
    GUISetState(@SW_DISABLE, $Main_GUI)
    $ret = DllCall("user32.dll", "int", "MessageBox", _
            "hwnd", $Main_GUI, _
            "str", $MsgBoxText, _
            "str", $MsgBoxTitle, _
            "int", $MsgBoxType)
    $err = @error
    GUISetState(@SW_ENABLE, $Main_GUI)
    If Not $err Then Return $ret[0]
EndFunc   ;==>_MsgBox

Func CharToOem($String)
    Local $tBuffer = DllStructCreate('char[' & StringLen($String)+1 & ']')
    DllCall('user32.dll','none','CharToOem','str',$String,'ptr',DllStructGetPtr($tBuffer))
    If @error Then Return SetError(@error, 0, 0)
    Return SetError(0, 0, DllStructGetData($tBuffer, 1))
EndFunc

-------
Создатель знает, что достиг совершенства не когда нечего добавить, а когда нечего убрать. - Антуан де Сент-Экзюпери

Это сообщение посчитали полезным следующие участники:

Отправлено: 09:00, 29-08-2009

 

Ветеран


Сообщения: 27449
Благодарности: 8087

Профиль | Отправить PM | Цитировать


Diamond, есть одно замечание: желательно упомянуть, что слияние отдельных частей созданным пакетным файлом желательно производить на разделе с файловой системой NTFS, поскольку именно там записи о файлах в MFT хранятся в упорядоченном виде, именно в таком порядке они будут возвращаться по FindFirst/NextFile и обрабатываться командой «copy …».

На файловых системах FATxx (FAT16/FAT32; насчёт FAT12, полагаю, озадачиваться сим бессмысленно даже теоретически , на exFat не проверялось) обработка будет идти в порядке создания записей файлов в каталоге, что может привести к неверным результатам слияния на разделах с этими файловыми системами.

Вероятность этого исчезающе мала, но, тем не менее, существует.

Отправлено: 06:33, 30-08-2009 | #11



Для отключения данного рекламного блока вам необходимо зарегистрироваться или войти с учетной записью социальной сети.

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


Пользователь


Сообщения: 135
Благодарности: 32

Профиль | Отправить PM | Цитировать


Iska, У меня на FAT32 объединяются без проблем, на NTFS проверить не могу(диск полностью "забит")

Изначально создаваемый пакетный файл не использовал подставные символы т.е. каждое имя части указывалось точно(part1+part2+... и т.д.), но в этом случае может возникнуть ошибка из-за ограничения на длину строки в пакетном файле. Ещё у меня возникала мысль использовать цикл "For" (в пакетном файле) чтобы обойти это ограничение но я отказался от этой идеи.

-------
Создатель знает, что достиг совершенства не когда нечего добавить, а когда нечего убрать. - Антуан де Сент-Экзюпери


Отправлено: 13:53, 30-08-2009 | #12


Ветеран


Сообщения: 27449
Благодарности: 8087

Профиль | Отправить PM | Цитировать


Цитата Diamond:
Изначально создаваемый пакетный файл не использовал подставные символы т.е. каждое имя части указывалось точно(part1+part2+... и т.д.), но в этом случае может возникнуть ошибка из-за ограничения на длину строки в пакетном файле. Ещё у меня возникала мысль использовать цикл "For" (в пакетном файле) чтобы обойти это ограничение но я отказался от этой идеи.
Один-в-один , коллега. С тем исключением, что тот самый пример из справки («copy /b *.exe combin.exe ») я обошёл своим вниманием по вышеупомянутой причине.

Вы отказались, а я сделал как раз с «FOR /L», и результат оказался весьма удручающ (на больших объёмах/количествах частей время росло в геометрической прогрессии, хотя, конечно, можно было и тут несколько сократить время, используя слияние попарно, но я этого не стал делать).

Цитата:
Iska, У меня на FAT32 объединяются без проблем, на NTFS…
Вот пример, на котором будет отчётливо видна существующая разница в порядке слияния между FAT и NTFS (для этого копируемые файлы в примере создаются в порядке, отличном от нарастающего):

читать дальше »
Код: Выделить весь код
@echo off

md ".\Test"
cd ".\Test"

echo 03>03.txt
echo 02>02.txt
echo 01>01.txt

copy *.txt result.dat
type result.dat

del result.dat
del *.txt

cd ".."
rd ".\Test"
Такое может возникнуть, например, при переносе частей на флэшке с машины на машину в раздел FAT в случайном (не нарастающем) порядке.

В принципе, я думаю, что можно (и нужно) побороться и с FAT, например, предварительно сделав перенос отдельных частей в правильном порядке во временную папку (хотя бы тем же «FOR /L»), а затем уже безболезненно выполняя «copy /b *.parts …». Во всяком случае, я у себя именно так попробую и сделать, что-то наподобие:

Код: Выделить весь код
@echo off
setlocal enableextensions enabledelayedexpansion

md ".\Test"
cd ".\Test"

echo 03>03.txt
echo 02>02.txt
echo 01>01.txt


set strTempFolder=$temp%random%
md ".\%strTempFolder%"

for /l %%i in (1,1,99) do (
    set number=00%%i
    set strPartFile=!number:~-2!.txt

    if exist "!strPartFile!" (
        move "!strPartFile!" ".\%strTempFolder%\!strPartFile!">nul
    )
)

copy /b ".\%strTempFolder%\*.txt" "result2.dat"
move ".\%strTempFolder%\*.txt" ".">nul
rd /s /q ".\%strTempFolder%"
echo [result2.dat] & type "result2.dat"
или, например, получая гарантированно отсортированный список файлов-частей посредством «dir /b /o:n ??.txt», или попросту задавая хранение списка частей в самом пакетном файле при его создании, и лишь потом делая шаманство «move в папку-combine-move назад».

В любом случае — отдельное спасибо: как бы то ни было, а Вы своим скриптом помогли мне осознать своё искреннее заблуждение в этом вопросе (что делать «copy /b *.parts …» всё-таки можно, выполнив предварительно некоторые телодвижения).
Это сообщение посчитали полезным следующие участники:

Отправлено: 21:48, 30-08-2009 | #13


Пользователь


Сообщения: 135
Благодарности: 32

Профиль | Отправить PM | Цитировать


Iska, Спасибо за подробное разъяснение ошибки слияния!
Отдельное спасибо за рабочий пример, имхо, главная привлекательность этой идеи, как раз и заключается в использовании пакетного файла.
+5

-------
Создатель знает, что достиг совершенства не когда нечего добавить, а когда нечего убрать. - Антуан де Сент-Экзюпери


Отправлено: 09:29, 31-08-2009 | #14


Новый участник


Сообщения: 12
Благодарности: 0

Профиль | Отправить PM | Цитировать


Люди подскажите а как таким же с помобом с помощью bat разбить файл на несколько маленьких, после определенного слова например конец?
Очень нужно

Отправлено: 17:08, 03-09-2012 | #15


Аватара для STARSsoft

Новый участник


Сообщения: 30
Благодарности: 1

Профиль | Отправить PM | Цитировать


Есть большой HTML файл. Одного текста только почти на 30 мегабайт. Разные части текста в файле разделены тегами-комментариями, например <!-- Post --> тут сам текст <!-- # Post -->.
Нужно каждую часть между этими тегами сохранить в отдельный файл. Имя файла не имеет значения, можно даже просто цифрами по порядку.
Сидеть и копипастить уже заморился, а таких файлов несколько.

Отправлено: 10:25, 14-05-2013 | #16


Аватара для AZJIO

Старожил


Сообщения: 254
Благодарности: 126

Профиль | Отправить PM | Цитировать


STARSsoft,

FileOperations.au3

Код: Выделить весь код
#include <FileOperations.au3>

$sPath = @ScriptDir & '\index.htm'
$sText = FileRead($sPath)
$aSplit = _StringSplitRegExp($sText, '<!-- Post -->.*?<!-- # Post -->')
For $i = 1 To $aSplit[0]
    $sPathNew = _FO_GetCopyName($sPath, 1)
    $hFile = FileOpen($sPathNew, 2)
    FileWrite($hFile, $aSplit[$i])
    FileClose($hFile)
Next

; http://www.autoitscript.com/forum/topic/139260-autoit-snippets/?p=1065198
; http://www.autoitscript.com/forum/topic/139260-autoit-snippets/page__st__140#entry1036931
; http://www.autoitscript.com/forum/topic/65662-stringsplitregexp/
; Автор ..........: AZJIO
Func _StringSplitRegExp($sString, $sPattern, $flag = 0, $sIncludeMatch = 0, $iCount = 0)
    Local $sSplit, $sDelim, $sReplace, $Toggle, $iPos = 1
    If IsBinary($sString) Then
        $sString = Hex($sString)
        $sDelim = Chr(1)
        Local $aError[2] = [1, $sString]
    Else
        Local $aError[2] = [1, $sString]
        For $i = 1 To 30
            $Toggle = Not $Toggle
            If $Toggle Then ; 1, 30, 3, 28    ...   27, 4, 29, 2
                $sDelim &= Chr($i)
            Else
                $sDelim &= Chr(32 - $i)
            EndIf
            $iPos = StringInStr($sString, $sDelim, 1, 1, $iPos) ; смещение позволяет найти разделитель за 1 проход
            If Not $iPos Then ExitLoop ; если вхождение не найдено, то разделитель сформирован
        Next
        If $iPos Then Return SetError(1, 0, $aError)
    EndIf
    Switch $sIncludeMatch
        Case 0
            $sReplace = $sDelim
        Case 1
            $sReplace = "$0" & $sDelim
        Case 2
            $sReplace = $sDelim & "$0"
    EndSwitch
    $sSplit = StringRegExpReplace($sString, $sPattern, $sReplace, $iCount)
    If @error Then Return SetError(2, @extended, $aError)
    If Not @extended Then Return SetError(3, 0, $aError)
    If $flag Then $flag = 2
    Return StringSplit($sSplit, $sDelim, 1 + $flag)
EndFunc   ;==>_StringSplitRegExp

Последний раз редактировалось AZJIO, 24-05-2013 в 09:34.

Это сообщение посчитали полезным следующие участники:

Отправлено: 19:28, 14-05-2013 | #17


Аватара для AZJIO

Старожил


Сообщения: 254
Благодарности: 126

Профиль | Отправить PM | Цитировать


STARSsoft, перечитал твой пост. Я ошибся, тот вариант использует текст как разделитель. Вот обычное регулярное выражение, которое возвратит текст между тегами в массив.

Код: Выделить весь код
#include <FileOperations.au3>
$sPath = @ScriptDir & '\index.htm'
$vText = FileRead($sPath)
$vText=StringRegExp($vText, '<!-- Post -->(.*?)<!-- # Post -->', 3)
For $i = 0 To UBound($vText)-1
    $sPathNew = _FO_GetCopyName($sPath, 1)
    $hFile = FileOpen($sPathNew, 2)
    FileWrite($hFile, $vText[$i])
    FileClose($hFile)
Next

Отправлено: 01:42, 17-05-2013 | #18



Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » AutoIt » Скрипт для разбиения файла на части

Участник сейчас на форуме Участник сейчас на форуме Участник вне форума Участник вне форума Автор темы Автор темы Шапка темы Сообщение прикреплено

Похожие темы
Название темы Автор Информация о форуме Ответов Последнее сообщение
[решено] Нужен скрипт для autoit для раскопирования файла irk72ag Автоматическая установка приложений 3 19-06-2009 15:27
Мультимедиа - [решено] Программа для разбиения mp3 по папкам arseniiavr Программное обеспечение Windows 4 06-02-2009 22:42
подгрузка части кода на html-страницу из отдельного файла shmond Вебмастеру 3 04-04-2007 16:43
Скрипт для поиска файла Pavlov10 Программирование и базы данных 3 02-03-2006 11:51
Как собрать 2 части avi-файла в один? mitino Хочу все знать 7 08-11-2003 00:14




 
Переход