Давно не писал, был занят. Недавно столкнулся с необходимостью отправить файл на сервер в автоматическом режиме. Сделал формочку, распарсил посмотрел закинул в WinHTTP и бамс ... сервер говорит что я формочку не отправил:) а длина данных вместо 400к всего 182 байта. Вообщем стал разбираться оказывается проблема была в пресловутых нулевых байтах которые были в бинариках отправляемых на сервер. Пришлось обратиться к манулу на тему как закодировать так, чтобы проходил.
Здесь нашел ответ - Base64, взял свой древний алгоритм (прилагать его не буду чтоб не дискридитировать себя, суть была в привидении 3*ASC => 24*BIN => 4*ASC), запустил ... ждал минуты 3 пока файл перекодируется, перекодировался, прошел. В принципе на этом можно было бы и закончить пост, но ждать 3 минуты - мне не понравилось. Начал искать, нашел вариант в сети - там я ждал 18 секунд, это уже было значительно интереснее по сравнению с 3 минутами, но всеже как-то медленно)) Решил разложить всё с нуля.
в форме решил потестировать различные варианты:
базовая идея преобразования заключалась в данном коде:
- Код: Выделить всё
B(1) = Mid$(IncStr,I,1)
B(2) = Mid$(IncStr,I + 1,1)
B(3) = Mid$(IncStr,I + 2,1)
'результирующая последовательность
R(1) = B(1) \ 4
R(2) = (B(1) Mod 4) * 16 + B(2) \ 16
R(3) = (B(2) Mod 16) * 4 + B(3) \ 64
R(4) = B(3) Mod 64
Res = Res & Mid$(Code,R(1),1) & Mid$(Code,R(2),1) & Mid$(Code,R(3),1) & Mid$(Code,R(4),1)
всё конечно получилось, только вот 100000 байт считывало 7 секунд, рисковать и проверять на 1Мб не решился)))
требовалось немного ускорить, в противном случае это грозило минутным ожиданием при отправке фотографии в 1М на сайт (только лишь на перекодировку).
Вообщем решил пойти по пути поэтапной оптимизации на форме для тестирования:
на форме кнопка, вот код:
- Код: Выделить всё
Dim A1 As Single
Dim A2 As Single
Dim A3 As Single
Dim A4 As Single
Dim SMB As String
Const N = 1048576
Dim ARR(63) As Byte
Private Const Code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
Private Sub Form_Load()
Erase ARR
For I = 0 To 63
ARR(I) = Asc(Mid$("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", I + 1, 1))
Next I
End Sub
Function Base64Encode2(ByRef ArrByte() As Byte) As String
Dim ResByte() As Byte
Dim B(3) As Byte
Dim R(4) As Byte
Dim DobSmb As Byte
Dim I As Long
Dim F As Long
Dim T As Long
DobSmb = (3 - (UBound(ArrByte) Mod 3)) Mod 3
ReDim Preserve ArrByte(UBound(ArrByte) + DobSmb)
F = UBound(ArrByte) \ 3
ReDim ResByte(UBound(ArrByte) / 3 * 4)
'IncStr = IncStr & String(DobSmb, 0)
For I = 1 To UBound(ArrByte) Step 3
B(1) = ArrByte(I)
B(2) = ArrByte(I + 1)
B(3) = ArrByte(I + 2)
R(1) = B(1) \ 4
R(2) = (B(1) Mod 4) * 16 + B(2) \ 16
R(3) = (B(2) Mod 16) * 4 + B(3) \ 64
R(4) = B(3) Mod 64
T = ((I - 1) \ 3) * 4
If F = (I + 2) \ 3 Then
ResByte(T + 1) = ARR(R(1))
ResByte(T + 2) = ARR(R(2))
Select Case DobSmb
Case 0:
ResByte(T + 3) = ARR(R(3))
ResByte(T + 4) = ARR(R(4))
Case 1:
ResByte(T + 3) = ARR(R(3))
ResByte(T + 4) = 61
Case 2:
ResByte(T + 3) = 61
ResByte(T + 4) = 61
End Select
Else
ResByte(T + 1) = ARR(R(1))
ResByte(T + 2) = ARR(R(2))
ResByte(T + 3) = ARR(R(3))
ResByte(T + 4) = ARR(R(4))
End If
Next I
Base64Encode2 = ResByte
End Function
Private Sub Command1_Click()
Dim ArrByte() As Byte
A1 = Timer
SMB = String(N, "A")
A2 = Timer
ArrByte = SMB
A3 = Timer
RES = Base64Encode2(ArrByte)
A4 = Timer
Erase ArrByte
Debug.Print "#2:", A2 - A1, A3 - A2, A4 - A3, A4 - A1
End Sub
вот результат тестирования из окна отладки:
- Код: Выделить всё
#2: 0 0 1,09375 1,09375
#2: 0 0,015625 1,09375 1,109375
#2: 0 0 1,09375 1,09375
#2: 0 0 1,09375 1,09375
#2: 0 0 0,84375 0,84375
#2: 0,015625 0 1,09375 1,109375
#2: 0,015625 0 1,09375 1,109375
#2: 0 0,015625 1,09375 1,109375
здесь видно что для прогона 1МБ потребовалось лишь от 0,84 до 1,093, этот результат мне понравился, возник лишь вопрос, а можно ли сделать быстрее? Как мне показалось - Да, можно. Попробовал избавиться от массивов R и B заменив их переменными R1..R4,B1..B3
получилось вот так:
- Код: Выделить всё
Function Base64Encode(ByRef ArrByte() As Byte) As String
Dim ResByte() As Byte
Dim B1 As Byte
Dim B2 As Byte
Dim B3 As Byte
Dim R1 As Byte
Dim R2 As Byte
Dim R3 As Byte
Dim R4 As Byte
Dim DobSmb As Byte
Dim I As Long
Dim F As Long
Dim T As Long
DobSmb = (3 - (UBound(ArrByte) Mod 3)) Mod 3
ReDim Preserve ArrByte(UBound(ArrByte) + DobSmb)
F = UBound(ArrByte) \ 3
ReDim ResByte(UBound(ArrByte) / 3 * 4)
'IncStr = IncStr & String(DobSmb, 0)
For I = 1 To UBound(ArrByte) Step 3
B1 = ArrByte(I)
B2 = ArrByte(I + 1)
B3 = ArrByte(I + 2)
R1 = B1 \ 4
R2 = (B1 Mod 4) * 16 + B2 \ 16
R3 = (B2 Mod 16) * 4 + B3 \ 64
R4 = B3 Mod 64
T = ((I - 1) \ 3) * 4
If F = (I + 2) \ 3 Then
ResByte(T + 1) = ARR(R1)
ResByte(T + 2) = ARR(R2)
Select Case DobSmb
Case 0:
ResByte(T + 3) = ARR(R3)
ResByte(T + 4) = ARR(R4)
Case 1:
ResByte(T + 3) = ARR(R3)
ResByte(T + 4) = 61
Case 2:
ResByte(T + 3) = 61
ResByte(T + 4) = 61
End Select
Else
ResByte(T + 1) = ARR(R1)
ResByte(T + 2) = ARR(R2)
ResByte(T + 3) = ARR(R3)
ResByte(T + 4) = ARR(R4)
End If
Next I
Base64Encode = ResByte
End Function
вот результат из окна отладки:
- Код: Выделить всё
#1: 0 0,015625 0,765625 0,78125
#1: 0 0 0,78125 0,78125
#1: 0 0,015625 0,765625 0,78125
#1: 0 0 0,78125 0,78125
#1: 0 0 0,78125 0,78125
#1: 0,015625 0 0,75 0,765625
#1: 0 0 0,78125 0,78125
#1: 0 0 0,53125 0,53125
Результат вдохновил, но можно ли лучше? )))
Прошу помощи)))