]>Кодирование base64

Кодирование base64

Аватар пользователя mabu

Base64 буквально означает — позиционная система счисления с основанием 64. Здесь 64 — это число символов в алфавите кодирования, из которого формируется конечный буквенно‐цифровой текст на основе латинского алфавита. Число соответствует наибольшей степени двойки (2^6), которая может быть представлена с использованием печатных символов ASCII.

  1. Зачем это нужно?
  2. Алгоритм
  3. Где же код?
  4. Использование

Зачем это нужно?

Так исторически сложилось, что многие форматы передачи и хранения данных (html, url схемы, xml, email и тому подобное) используют текст вместо бинарных кодов. Но если формат передачи данных текстовый, а передать необходимо бинарные данные (отдельно либо вместе с текстовыми данными) — тут на помощь приходит base64. Например:

И это не исчепывающий список, где применяется base64.

Алгоритм

Алгоритм кодирования прост. Берётся тройка октетов. 3 откета по 8 бит — это 24 бита. С другой стороны 24 бита — это 4 раза по 6 бит. Получившиеся четыре шестибитных числа представляют индексы символов в следующей строке:


ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

Для примера закодируем строку «Man» в base64.

Исходные данные: строка «Man»

Коды символов:7797110

Двоичный вид с группировкой по 8 бит:010011010110000101101110

Двоичный вид с группировкой по 6 бит:010011010110000101101110

Полученные индексы в base64:1922546

Конечный результат в Base64:TWFu

Если длина результирующей строки не кратна четырём, то её дополняют необходимым количеством символов «=».

Где же код?

Довольно теоретизирований, перейдём к коду.

Заголовочный файл base64.bi:

Код FreeBASIC
' Кодирование в Base64 и обратно
#ifndef unicode
&t;#define unicode
#endif
#include once "windows.bi"
#include once "win\shlwapi.bi"

Const B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
Const Base64StringLength As Integer = 19

' Шифрует массив байт в base64
' sOut — указатель на буфер под результирующую строку
' sEncodedB — указатель на массив байт, которые нужно закодировать
' BytesCount — количество байт в массиве
' Функция может записать за выделенный буфер, если он будет слишком мал
' Размер требуемого буфера под результирующую строку должен быть не менее ((BytesCount \ 3) + 1) * 4 символов + 1 символ под нулевой
' Функция записывает завершающий ноль
' Возвращает количество символов (без учёта завершающего нуля)
Declare Function Encode64( _
&t;&t;ByVal sOut As WString Ptr, _
&t;&t;ByVal sEncodedB As UByte Ptr, _
&t;&t;ByVal BytesCount As Integer _
)As Integer

' Дешифрует из base64 в массив байт
' b — указатель на буфер, куда будет записан результат дешифровки
' s — указатель на base64‐строку
' Возвращает количество записанных в буфер байт
' Размер буфера должен быть достаточным для записи в него данных
Declare Function Decode64( _
&t;&t;ByVal b As UByte Ptr, _
&t;&t;ByVal s As WString Ptr _
)As Integer

Файл base64.bas:

Код FreeBASIC
#include once "base64.bi"

Function E0(ByVal v1 As UByte)As UByte
&t;' Получить шесть левых бит числа
&t;Return v1 Shr 2
End Function

Function E1(ByVal v1 As UByte, ByVal v2 As UByte)As UByte
&t;' Получить два правых бита первого числа и четыре левых бита второго числа
&t;Return ((v1 And &b00000011) Shl 4) + (v2 Shr 4)
End Function

Function E2(ByVal v2 As UByte, ByVal v3 As UByte)As UByte
&t;' Получить четыре правых бита первого числа и два левых бита второго числа
&t;Return ((v2 And &b00001111) Shl 2) + (v3 Shr 6)
End Function

Function E3(ByVal v3 As UByte)As UByte
&t;' Получить шесть правых бит числа
&t;Return v3 And &b00111111
End Function

Function Encode64(ByVal sOut As WString Ptr, ByVal sEncodedB As UByte Ptr, ByVal BytesCount As Integer)As Integer
&t;Dim j As Integer = 0
&t;Dim k As Integer = 0
&t;' Количество байт, не умещающихся в тройку байт
&t;Dim ELM3 As Integer = BytesCount Mod 3
&t;' Идти через каждые три байта
&t;For j = 0 To BytesCount - ELM3 - 1 Step 3
&t;&t;sOut[k + 0] = (@B64 + E0(sEncodedB[j + 0]))[0]
&t;&t;sOut[k + 1] = (@B64 + E1(sEncodedB[j + 0], sEncodedB[j + 1]))[0]
&t;&t;sOut[k + 2] = (@B64 + E2(sEncodedB[j + 1], sEncodedB[j + 2]))[0]
&t;&t;sOut[k + 3] = (@B64 + E3(sEncodedB[j + 2]))[0]
&t;&t;k += 4
&t;Next

&t;Select Case ELM3
&t;&t;Case 1
&t;&t;&t;sOut[k + 0] = (@B64 + E0(sEncodedB[j + 0]))[0]
&t;&t;&t;sOut[k + 1] = (@B64 + E1(sEncodedB[j + 0], sEncodedB[j + 1]))[0]
&t;&t;&t;sOut[k + 2] = &h3D
&t;&t;&t;sOut[k + 3] = &h3D
&t;&t;&t;k += 4
&t;&t;Case 2
&t;&t;&t;sOut[k + 0] = (@B64 + E0(sEncodedB[j + 0]))[0]
&t;&t;&t;sOut[k + 1] = (@B64 + E1(sEncodedB[j + 0], sEncodedB[j + 1]))[0]
&t;&t;&t;sOut[k + 2] = (@B64 + E2(sEncodedB[j + 1], sEncodedB[j + 2]))[0]
&t;&t;&t;sOut[k + 3] = &h3D
&t;&t;&t;k += 4
&t;End Select
&t;' Поставить завершающий ноль
&t;sOut[k] = 0
&t;Return k
End Function

Function GetBase64Index(ByVal sChar As Integer)As Integer
&t;If sChar = 0 Then
&t;&t;Return -1
&t;End If
&t;Dim w As WString Ptr = StrChr(@B64, sChar)
&t;If w = 0 Then
&t;&t;Return -1
&t;End If
&t;Return w - @B64
End Function

' Пропускаем все символы не из набора
Function SkipWrongChar(ByVal s As WString Ptr)As Integer
&t;Dim i As Integer = 0
&t;Dim sChar As Integer = s[i]
&t;Do Until sChar = 0
&t;&t;If GetBase64Index(sChar) <> -1 Then
&t;&t;&t;Exit Do
&t;&t;End If
&t;&t;i += 1
&t;&t;sChar = s[i]
&t;Loop
&t;Return i
End Function

Function CalculateString(ByVal b As UByte Ptr, ByVal BytesCount As Integer, ByVal w1 As Integer, ByVal w2 As Integer, ByVal w3 As Integer, ByVal w4 As Integer)As Integer
&t;If w2 > -1 Then
&t;&t;b[BytesCount] = (w1 * 4 + w2 \ 16) And 255
&t;&t;BytesCount += 1
&t;End If
&t;If w3 > -1 Then
&t;&t;b[BytesCount] = (w2 * 16 + w3 \ 4) And 255
&t;&t;BytesCount += 1
&t;End If
&t;If w4 > -1 Then
&t;&t;b[BytesCount] = (w3 * 64 + w4) And 255
&t;&t;BytesCount += 1
&t;End If
&t;Return BytesCount
End Function

Function Decode64(ByVal b As UByte Ptr, ByVal s As WString Ptr)As Integer
&t;Dim BytesCount As Integer = 0
&t;Dim length As Integer = Len(*s)
&t;For i As Integer = 0 To length - 1 Step 4
&t;&t;Dim ww As Integer = Any
&t;&t;' Необходимо пропустить все символы не из набора
&t;&t;i += SkipWrongChar(s[i + 0])
&t;&t;If i >= length - 0 Then
&t;&t;&t;Return BytesCount
&t;&t;End If
&t;&t;ww = s[i + 0]
&t;&t;Dim w1 As Integer = GetBase64Index(ww)

&t;&t;i += SkipWrongChar(s[i + 1])
&t;&t;If i >= length - 1 Then
&t;&t;&t;Return CalculateString(b, BytesCount, w1, 0, 0, 0)
&t;&t;End If
&t;&t;ww = s[i + 1]
&t;&t;Dim w2 As Integer = GetBase64Index(ww)

&t;&t;i += SkipWrongChar(s[i + 2])
&t;&t;If i >= length - 2 Then
&t;&t;&t;Return CalculateString(b, BytesCount, w1, w2, 0, 0)
&t;&t;End If
&t;&t;ww = s[i + 2]
&t;&t;Dim w3 As Integer = GetBase64Index(ww)

&t;&t;i += SkipWrongChar(s[i + 3])
&t;&t;If i >= length - 3 Then
&t;&t;&t;Return CalculateString(b, BytesCount, w1, w2, w3, 0)
&t;&t;End If
&t;&t;ww = s[i + 3]
&t;&t;Dim w4 As Integer = GetBase64Index(ww)

&t;&t;BytesCount = CalculateString(b, BytesCount, w1, w2, w3, w4)
&t;Next
&t;Return BytesCount
End Function

Использование

Функция Encode64 кодирует массив байт в base64‐строку. Для этого ей нужно передать указатель буфер под результирующую строку, указатель на массив байт и размер массива.

Код FreeBASIC
#ifndef unicode
#define unicode
#endif
' Для преобразования строки из utf-16 в utf-8
#include once "windows.bi"

Const BufferLength As Integer = 1024
Const StringToConvert = "Всем привет! Это сообщение будет закодировано."

' Строка, куда будет записана base64‐строка, плюс один символ под нулевой
Dim base64 As WString * (BufferLength + 1) = Any

' Массив байт
Dim bytes(BufferLength * SizeOf(WString)) As UByte = Any

' Нужно получить из строки массив байт
' Для этого её нужно преобразовать в utf-8
Dim BytesCount As Integer = WideCharToMultiByte(CP_UTF8, 0, @StringToConvert, -1, @bytes(0), BufferLength * SizeOf(WString), 0, 0) - 1

' Закодировать массив байт в bas64
Encode64(@base64, @bytes(0), BytesCount)
Print base64

' Преобразовать из base64 в массив байт
BytesCount = Decode64(@bytes(0), @base64)

' Из массива байт в строку, результат будет в base64
bytes(BytesCount) = 0
MultiByteToWideChar(CP_UTF8, 0, @bytes(0), -1, base64, BufferLength)
Print base64

В примере используются функции WideCharToMultiByte и MultiByteToWideChar для преобразования строки в массив байт и обратно. Если у тебя есть уже заранее оформленный массив байт, например, данные файла, то эти функции использовать не нужно. В качестве домашнего задания предлагаю написать простую утилиту, кодирующую и декодирующую любые файлы в base64.

Поделись ссылочкой в социальных сетях