Консультация № 170105
02.07.2009, 19:54
0.00 руб.
0 13 1
Здравствуйте!
Мне необходимо написать программу, которая будет "сливать" два файла, т.е. копировать содержимое одного файла в другой.
Для проверки корректности передачи пробовал копировать содержимое одного файла в пустой файл.
У меня это получилось, но все время получается так, что у меня копируются "лишние" байты, т.е. если копировать содержимое одного файла в другой пустой файл, то размер файла, куда мы копировали, превышает размер исходного файла на несколько байт. Как выяснилось, это связано с тем, какими объемами мы берем информацию из исходного файла, но даже, если брать по байту, то получится, что размер файла, куда мы писали, будет на байт больше того, из которого писали. Язык программирования: Visual Basic 6.
Вот код моей программы, никаких объектов на форме нет:

Private Sub Form_Load()
Dim MyF, MyFF
Dim A(1023) As Byte
Dim L As Long
L = FileLen(App.Path & "\1.bmp") + 1
MyF = FreeFile
Open App.Path & "\1.bmp" For Binary As #MyF
MyFF = FreeFile
Open App.Path & "\2.bmp" For Binary As #MyFF
Do Until EOF(MyFF)
Get #MyFF, , A
Put #MyF, L, A
L = L + 1024
Loop
Close #MyF
Close #MyFF
MsgBox "Готово!!", vbInformation, "Готово!"
End
End Sub

Большое спасибо!

Обсуждение

Неизвестный
02.07.2009, 20:37
общий
Так, вот, вопрос в том, как убрать эти "лишние" байты?
давно
Профессионал
848
1596
02.07.2009, 22:26
общий
для начала вы читаете 2.bmp и переписывете в 1.bmp но при этом отталкиваетесь от размера 1.bmp...видимо перепутали.
и лишние байты думаю образуются от того что вы читаете куски по 1024 байта. Если файл не был кратен 1024 -то он и увеличивается до кратности в 1024.
также не нужно вычислять смещение в Put #MyF, L, A -указатель сам смещается, можно упростить до
Код:
Dim MyF, MyFF
Dim A(1023) As Byte
MyF = FreeFile
Open "E:\2.bmp" For Binary As #MyF
MyFF = FreeFile
Open "e:\1.bmp" For Binary As #MyFF
Do Until EOF(MyFF)
Get #MyFF, , A
Put #MyF, , A
Loop
Close #MyF
Close #MyFF
MsgBox "Ãîòîâî!!", vbInformation, "Ãîòîâî!"
End

====
если осуществлять побайтовое копирование, то нужно несколько изменить код -нужно не выполнять Put #MyF, , A -если флаг установился на EOF(MyFF) при Get #MyFF, , A.
побайтовое копирование выполняется дольше.
Код:
Dim MyF, MyFF
Dim A As Byte
MyF = FreeFile
Open "E:\2.bmp" For Binary As #MyF
MyFF = FreeFile
Open "e:\1.bmp" For Binary As #MyFF
Do
Get #MyFF, , A
If EOF(MyFF) Then Exit Do
Put #MyF, , A
Loop
Close #MyF
Close #MyFF
MsgBox "Ãîòîâî!!", vbInformation, "Ãîòîâî!"
End

давно
Профессионал
848
1596
02.07.2009, 22:31
общий
.пример побайтового копирования копирует размер байт в байт. если вы хотите копировать массивом байтов как в 1 варианте то нужно контролировать последнюю загрузку в массив, т.к. загружаемый последний кусок будет меньше. Нужно будет делать переразмерность массива(ReDim)
Неизвестный
03.07.2009, 06:15
общий
это ответ
Здравствуйте, AkaProc.
PsySex уже все рассказал, дополню:

Для не очень больших файлов, вполне допустимо сразу делать буфер размером с файл:
Код:

Sub AppendSmall(ByVal srcPath As String, ByVal dstPath As String)
Dim src As Integer
Dim dst As Integer
Dim buf() As Byte

If FileLen(srcPath) = 0 Then Exit Sub

src = FreeFile
Open srcPath For Binary Access Read Lock Read As #src
ReDim buf(LOF(src) - 1)
Get src, , buf
Close #src

dst = FreeFile
Open dstPath For Binary Access Write Lock Write As #dst
Put #dst, LOF(dst) + 1, buf
Close #dst

End Sub


Для файлов побольше(максимальный размер файлов все равно ограничен максимальным значением Long ~2Gb), одноразовым чтением уже не обойдемся:
Код:

Const DEFAULT_BUFSIZE = 4096

Function Min(ByVal a As Long, ByVal b As Long) As Long
If a < b Then Min = a Else Min = b
End Function

Sub AppendBig(ByVal srcPath As String, ByVal dstPath As String, Optional ByVal bufSize As Long = DEFAULT_BUFSIZE)
Dim src As Integer
Dim dst As Integer
Dim buf() As Byte
Dim srcSize As Long
If FileLen(srcPath) = 0 Then Exit Sub

src = FreeFile
Open srcPath For Binary Access Read Lock Read As #src
srcSize = LOF(src)

dst = FreeFile
Open dstPath For Binary Access Write Lock Write As #dst
Seek #dst, LOF(dst) + 1

If bufSize <= 0 Then bufSize = DEFAULT_BUFSIZE
bufSize = Min(bufSize, srcSize)
Do
ReDim buf(bufSize - 1)
Get #src, , buf
Put #dst, , buf
bufSize = Min(bufSize, srcSize - Loc(src))
Loop While bufSize > 0

Close #src
Close #dst

End Sub


Успехов.
Неизвестный
03.07.2009, 12:16
общий
Спасибо за помощь! Я сегодня еще подумаю над программой, может быть, предложу свой код. Ваши идеи мне очень понравились!
Неизвестный
03.07.2009, 13:48
общий
Я поработал и придумал вот такой код, работает быстро, т.е. файлы в 498 мб копирует за секунды, буфферизует по 5 мб. Функцию EOF я не использовал. Я использовал обычный цикл (FOR). Никаких "лишних" байт не образуется.
Пока он справедлив для файлов, которые больше или равны 5 Мб.
Вот мой код:
Private Sub Form_Load()
Dim MyF, MyFF
Dim A() As Byte
ReDim A(5242879) As Byte
Dim L, L1 As Long
L = FileLen(App.Path & "\2.bmp")
L1 = Int(L / 5242880)
MyF = FreeFile
Open App.Path & "\1.bmp" For Output As #MyF
Close #MyF
Open App.Path & "\1.bmp" For Binary As #MyF
MyFF = FreeFile
Open App.Path & "\2.bmp" For Binary As #MyFF
For k = 1 To L1
Get #MyFF, , A
Put #MyF, , A
Next
L1 = L - Loc(MyFF)
ReDim A(L1 - 1) As Byte
Get #MyFF, , A
Put #MyF, , A
Close #MyF
Close #MyFF
MsgBox "Готово!!", vbInformation, "Готов!"
End
End Sub
Неизвестный
03.07.2009, 14:15
общий
Я еще немножко поработал и сделал код, который работает для файлов любого размера, но главное, чтобы он не превышал 2 Гб, т.к. операторы get и put работают с типом Long. Кстати, не знаете, как обойти это ограничение?
****Я не ставлю тег "код" потомучто очень неудобно копировать код из этого окошка и еще он не разбивает его на строки и копирует его как одна строка, т.е. большой код получается единой строкой после вставки его в редактор. Да, и коды не такие большие.
Вот мой код:
Private Sub Form_Load()
Dim MyF, MyFF
Dim A() As Byte
ReDim A(5242879) As Byte
Dim L, L1 As Long

If FileLen(App.Path & "\2.jpg") >= 5242880 Then
L = FileLen(App.Path & "\2.jpg")
L1 = Int(L / 5242880)
MyF = FreeFile
Open App.Path & "\1.jpg" For Output As #MyF
Close #MyF
Open App.Path & "\1.jpg" For Binary As #MyF
MyFF = FreeFile
Open App.Path & "\2.jpg" For Binary As #MyFF
For k = 1 To L1
Get #MyFF, , A
Put #MyF, , A
Next
L1 = L - Loc(MyFF)
ReDim A(L1 - 1) As Byte
Get #MyFF, , A
Put #MyF, , A
Close #MyF
Close #MyFF
MsgBox "Готово!!", vbInformation, "Готов!"
End
Else
MyF = FreeFile
Open App.Path & "\1.jpg" For Output As #MyF
Close #MyF
Open App.Path & "\1.jpg" For Binary As #MyF
MyFF = FreeFile
Open App.Path & "\2.jpg" For Binary As #MyFF
ReDim A(FileLen(App.Path & "\2.jpg") - 1) As Byte
Get #MyFF, , A
Put #MyF, , A
Close #MyF
Close #MyFF
MsgBox "Готово!!", vbInformation, "Готов!"
End
End If
End Sub
Можете прокомментировать мой код, т.е. как на Ваш взгляд, есть ли у него какие-нибудь серьезные недостатки?
давно
Профессионал
848
1596
03.07.2009, 22:09
общий
Dim L, L1 As Long
не забываем что при таком объявлении L будет типом variantПравильно так
Dim L As Long, L1 As Long
Да а так норм :-)
Неизвестный
03.07.2009, 23:09
общий
Спасибо за совет! :)
Неизвестный
04.07.2009, 14:00
общий
Если не сложно, подскажите, пожалуйста, как можно обойти ограничение у операторов put и get, т.е. чтобы считывать файлы бОльших размеров, чем 2 ГБ ?
давно
Профессионал
848
1596
04.07.2009, 14:56
общий
AkaProc:
http://bbs.vbstreets.ru/viewtopic.php?f=1&t=27822
видимо нужно копать в сторону API
Неизвестный
06.07.2009, 02:36
общий
А что это за "волшебное число" 5242879? откуда оно взялось, и почему именно от этого числа зависит логика работы?
Зачем вообще "тянуть" во второй вариант(для всех файлов) вариант для файлов размером менее 5242880?
Какая-то логика в именовании переменных как MyF и MyFF, конечно существует, но, ИМХО, читабельность и защита от опечатки - страдают...
Почему размер буфера у Вас зависит от размера второго файла?
ReDim A(FileLen(App.Path & "\2.jpg") - 1) As Byte

Неизвестный
06.07.2009, 13:12
общий
1) Это число - размер буффера в байтах (5 МБ), логика работы зависит от него потомучто если у нас будет файл, размер которого НЕ кратен 5 МБ, то при копировании будут образовываться "лишние" байты, т.е. размер файла будет увеличиваться до кратного 5 МБ. Если файл более 5 МБ, то сначала мы вычисляем, сколько нам надо сделать циклов, чтобы скопировать целые части по 5 МБ, т.е. без "лишних" байт, а потом вычисляем, сколько нам еще осталось копировать и создаем массив размером с оставшуюся инфу в файле. Т.о. мы получаем быстрое копирование файла БЕЗ "лишних" байт.
2) Да, второй вариант презназначен для файлов любых размеров.
3) Насчет названий переменных я с Вами согласен - я всегда умел назвать переменные оригинально. :)
4) Размер буффера зависит от размера 2 файла потомучто мы 2 файл копируем в 1.
Можно было бы сделать прогу в виде процедур, но я хотел добиться работоспособности, на это заморачиваться не стал.
Форма ответа