скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

Base64 для непродвинутых



Автор: Александр Терехов

Вступление

Изначально для передачи электронной почты в Интернет использовался только текст (RFC822). Затем, с развитием компьютерных девайсов, потребовалась возможность передачи нетекстовой информации: аудио, видео, графических файлов, файлов приложений и т.д. Однако почтовые сервера как понимали только текст, так и остались понимать только его. Поэтому появилась необходимость каким-то образом преобразовать двоичный файл в текстовый. Вообще-то способ такого преобразования уже имел место - это UUE кодирование. Но появился еще один - base64. Этот способ используется в спецификации MIME (RFC2045-2049).

Что такое MIME? Если говорить вкратце, то это стандарт описания заголовков e-mail сообщений. Используя этот стандарт, в одном письме можно отправить сразу несколько различных вложений. Например, можно положить в аттачмент письма архивированный файл, оформить сообщение как просто текст, а также поместить HTML-страницу. И все это отправить получателю. Почтовая программа-получатель, понимающая MIME, совершенно свободно из файла электронной почты (который на самом деле является "обычным" текстовым файлом) извлечет архив, покажет сообщение и обработает тэги HTML. Некоторые почтовики, например Outlook Express, на радость вирмейкерам без спроса пользователя еще и запустят вложенные в HTML-страницу скрипты.

Идеология base64

Как известно, байт состоит из восьми битов :)

В один байт можно вложить 256 цифр, от 0 до 255. Однако, если вместо восьми байт использовать только шесть, то объем вложенной информации уменьшается до 64 цифр, от 0 до 63. Теперь главное: любую цифру 6-ти битового байта можно представить в виде печатного символа. 64 символа это не так много, us-ascii символов вполне хватит. Ниже представлен 64-х символьный base64 "алфавит".

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

где код символа A - 0, а код символа / - 63. Вроде, понятно. Что дальше?

А далее берутся три последовательных байта по восемь бит (всего 24 бита), и побитно делятся на четыре 6-ти битных байта (всего 24 бита). Немного странно звучит: "шестибитный байт". На самом деле бит восемь, однако используются только 6 младших бит, два старших бита игнорируются. Схематично такое "деление три к четырем" можно представить себе так:

В приведенном примере три числа 103, 193 и 58 были закодированы в base64 формат. В результате мы получили 4-х символьный стринг Z8E6. Т.о. на практике увидели идеологию перевода двоичной информации в текст по принципу 3 к 4.

Основываясь на этом принципе, мы можем закодировать любую двоичную информацию в текст, причем не очень сильно увеличивая ее объем (на 30%). Затем наша информация через почтовый сервер попадет к нужному адресату, почтовик которого декодирует текст в двоичный файл.

Все довольны, все смеются.

"Но..", скажите Вы, - "что делать, если у нас нет трех байтов? Есть только один или два!" В этом случае в конец четырех символьного стринга добавляется символ = (равно). Если не хватает (до трех) одного байта, то добавляется один символ "равно":

Z8E=

если не хватает двух байт, то добавляются два символа "равно":

Z8==

Что радует: с символами "равно" надо разбираться только один раз - при чтении конца файла. На этом вроде бы все... Нет, еще не все. Формат base64 имеет ограничение - общая длина строки, состоящая из 4-х символьных стрингов составляет 72 символа (за исключением самой последней строки - там уже сколько получится).

Для тех, кто не особо понял, о чем выше шла речь, рекомендую сделать следующее:

  1. Имеющейся почтовой программой сохранить в файл какое-нибудь письмо. Письмо, естественно, надо выбрать со вложенным файлом. При сохранении письма, тип файла следует выбрать "почтового формата" (e-mail message), например *.msg или *.eml
  2. Включить самый мощный текстовый редактор - Notepad ("Блокнот") и открыть сохраненный файл письма. Тем, у кого п.2 не получился с первого раза - тип файла при открытии его "Блокнотом" надо указать "Все файлы" (*.*)
  3. В "Блокноте" (а если файл большой, то в WordPad'е) откроется примерно нижеследующее:
Date: Sat, 12 Oct 2002 10:09:59 +0000
From: "Mr.Dark" 
X-Mailer: The Bat! (v1.47 Halloween Edition)
Reply-To: "Mr.Dark" 
Organization: Dark Company
X-Priority: 3 (Normal)
Message-ID: <5614244603.20021012100959@online.ru>
To: =?koi8-r?B?5czFzsEg5snMydDQz9fB?= 
Subject: Base64
Mime-Version: 1.0
Content-Type: multipart/mixed; boundary="----------73121211AFBA8E3"

------------73121211AFBA8E3
Content-Type: text/plain; charset=koi8-r
Content-Transfer-Encoding: 8bit

Hello еМЕОБ,

-- 
Best regards,
Mr.Dark                        mailto:dark@online.ru                                
Delphi-РТПЗТБННЙТПЧБОЙЕ        http://www.inta.portal.ru/dark/
------------73121211AFBA8E3
Content-Type: image/jpeg; name="ex1.jpg"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="ex1.jpg"

/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEP
ERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4e
Hh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCADFApsDASIA
AhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA
  ============================вырезано==================================
FABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUU
AFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQA
UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAf/Z

------------73121211AFBA8E3--

Полный текст можно посмотреть в файле EMail.txt

Content, boundary и все остальное, что идет после строки Mime-Version: 1.0 - это стандарт MIME. Буквы, написанные в koi8-r, читаются в виде абракадабры. И это правильно, т.к. кодировка настоящего документа установлена в windows-1251. Если в Вашем броузере установить кодировку koi8-r (в Internet Explorer 5.0 в главном меню выбрать "Вид"-"Вид кодировки"-"Кириллица (КОИ8-Р)"), то эти буквы можно будет прочитать. Остальной текст, правда, разобрать вряд ли удастся :) В конце текста письма находится очень большой блок еще одной абракадабры. В приведенном тексте он немного порезан. Так вот этот блок и есть двоичный файл закодированный base64 в текст. Чуть выше "большого блока" видно, что для кодировки применялся base64, видны также названия файла. То, что выше - реальное название файла и его тип: 'ex1.jpg', тип файла image/jpeg, которое пониже - название этого файла в окне аттача письма. Теперь, надеюсь, все понятно. Да, еще. Немного отвлекаясь от base64, скажу, что чтение заголовков писем - наиувлекательнейшее занятие. Например, с помощью одной из моих программ (IPScaner, лежит на моем сайте в разделе "Программы"), по заголовку письма можно очень даже конкретно определить географическое место отправки e-mail. Однажды вычислил даже номер кабинета в одном из московских институтов, откуда ко мне пришло письмо.

Алгоритм base64 кодирования и декодирования.

Наверное существует самый оптимальный и быстрый алгоритм кодирования и декодирования base64. Но... Почему-то хочется в очередной раз самому изобрести велосипед... Нисколько не претендуя на оптимальность, скорострельность и оригинальность... Итак, рассматривая идеологию base64, первое, что приходит в голову - это устроить небольшой битовый конвейер. Т.е. (для случая кодирования) взять 8-ой (старший) бит исходного байта и поместить его в начало 6-ти битового байта (по поводу термина "6-ти битовый байт" см. выше). Затем на место 8-го бита исходного байта поместить 7-й бит, а в 6-ти битовом байте первый бит (младший) переместить на место 2-го бита. После такого перемещения освобождается первый (младший) бит 6-ти битного числа. В него и поместим старший (бывший седьмой) бит исходного байта. Затем еще раз передвинем биты в обоих байтах. И т.д. Примерно так:

Рассмотрим шаг 1.

Как известно стандартные операнды Паскаля AND и OR могут выступать в двух ипостасях: битовая арифметика и логика. В первом шаге используем операнд AND как битовую ипостась :)

Для проверки установки старшего бита исходного 8-ми битного байта наложим на него так называемую "маску". Т.е. применим к нему побитовую операцию AND с числом 128 (10000000).

Как видно из приведенной схемы, проверить установку старшего бита совсем несложно. Если результатом операции получается число 128, значит бит установлен, а если в результате получаем 0, значит старший бит не установлен. Далее в зависимости от полученного результата применим к 6-ти битовому байту битовую операцию OR с числом 1, которая в любом случае применения устанавливает первый (младший) байт в 1.

Здесь необходимо учитывать то, что перед применением побитовой операции OR с числом 1 младший бит 6-ти битового байта всегда 0. В случае для первых двух шагов потому, что мы сами обнуляем его при инициализации, для последующих шагов, потому что применяем операцию SHR.

Итак, посмотрим как выглядит реализация первого шага в Паскале:


if (Source and 128) = 128 then
  Destination := Destination or 1;

где Source - 8-ми битный байт источник, а Destination - 6-ти битный байт приемник.

Шаг 2.

Сместим первый бит 6-ти битного байта на вторую позицию, одновременно обнуляя его первый бит.

Destination:=Destination SHR 1;

Сместим седьмой байт 8-ми битного байта на 8-е(старшее) место.

Source:=Source SHR 1;

Операция X SHR Y, смещает в байте X биты на Y-мест влево и автоматически устанавливает Y-младшие байты в 0, следовательно значение числа изменяется. Поэтому в реальном коде мы будем работать не с самим байтом-источником, а с его временной копией.

Дальнейшие шаги.

Повторяем шаги 1 и 2 шесть раз для каждого бита 6-ти битного байта. Одновременно необходимо следить за тем, что если мы обработали восемь бит байта-источника, следует перейти к новому байту. И последнее - если обработаны все три байта-источники, следует выйти из функции кодирования.

Теперь о декодировании.

Декодирование по сути ничем не отличается от кодирования. Делаем тоже самое, только источник и приемник меняются местами. А битовый конвейер должен продолжать работать в ту же сторону - справа - на лево: проверяем установку первого бита 6-ти битного байта (маску в этом случае надо накладывать с числом 32 (0010000) - не восемь битов, а шесть) и в зависимости от результата устанавливаем (или не устанавливаем) младший байт 8-ми битного байта. Затем делаем побитовый сдвиг влево.

Теперь, когда мы теоретически подковались можно посмотреть и исходный код, который находится в файле Base64Unit.pas

Примеры.

В первом примере (b64_Example1 5К) показана работа модуля base64-кодирования (декодирования). Три восьмибитных байта кодируются в 4-х символьный стринг и обратно. Понятно, что это всего лишь пример и при вводе цифр и букв следует соблюдать base64-алфавит.

Второй пример (b64_Example2 5К) - вполне работоспособная утилита, позволяющая кодировать любой файл в текстовый base64-формат. Кроме этого утилита позволяет base64-кодированный текст декодировать в двоичный файл. Например, Вы можете "вырвать" из тела e-mail сообщения base64-фрагмент и сохранить его в текстовом файле. А затем декодировать это файл в двоичный файл. Название файла и его расширение можно увидеть в MIME-секции письма - filename.

Конечно, при кодировании надо считывать по 58 байт, а не по 3, как это сделано в примере. Считывание по 58 байт значительно увеличит "скорострельность" утилиты. Но следует учитывать, что это всего лишь пример.

Пример очередного изобретения велосипеда :)






Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook