UUE кодирование

Материал из DRKB


Примечание
Данный пример рекомендуется использовать только для изучения, поскольку он не оптимален и не годится для больших объемов данных.

Для того, чтобы ОНО заработало, необходимо создать проект в составе:

  • Форма (Form) - 1 шт.
  • Поле ввода (Edit) - 2 шт., используются события OnDblClick.
  • Кнопка (Button) - 1 шт., используется событие OnClick.
  • Диалог открытия файла (Open Dialog) - 1 шт.
  • Диалог сохранения файла (Save Dialog) - 1 шт.

Имена файлов будут вводится либо вручную, либо из диалога (double-click на поле ввода Edit), причем в Edit1.Text должно лежать имя входного файла, в Edit2.Text - выходного. По нажатии кнопки пойдет процесс, который завершится сообщением "DONE."

Всего хорошего.

P. S. Функция ToAnySys обнаружена в книге "Для чего нужны и как работают персональные ЭВМ" от 1990 г. Там она присутствует в виде программы на BASIC'e.

P.P.S. Для стимулирования фантазии читателей "Советов..." высылаю так же мессагу из эхи, на основе которой я сваял свое чудо.


Файл Unit1.pas

// UUE кодирование
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtDlgs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    OpenDialog1: TOpenDialog;
    SaveDialog1: TSaveDialog;
    procedure Edit1DblClick(Sender: TObject);
    procedure Edit2DblClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

const
  ssz = (High(Cardinal) - $F) div SizeOf(Byte);
  // эта константа используется при выделении памяти

  p: string = '0123456789ABCDEF';
  // эта константа используется функцией toanysys

// выбор входного файла
procedure TForm1.Edit1DblClick(Sender: TObject);
begin
  if OpenDialog1.Execute then
    Edit1.Text := OpenDialog1.Filename;
end;

// выбор выходного (UUE) файла
procedure TForm1.Edit2DblClick(Sender: TObject);
begin
  if SaveDialog1.Execute then
    Edit2.Text := SaveDialog1.Filename;
end;

// выделение подстроки
function Mid(s: string; fromc, toc: Byte): string;
var
  s1: string;
  i: Byte;
begin
  s1 := '';
  for i := fromc to toc do
    s1 := s1 + s[i];
  mid := s1;
end;

// перевод числа (a) из десятичной системы в другую
// с основанием (r)
function ToAnySys(a, r: Byte): string;
var
  k: string;
  n, m, i: Byte;
begin
  Result := '';
  m := 1;
  while m <> 0 do
  begin
    m := a div r;
    n := a - m * r + 1;
    k := p[n];
    Result := k + Result;
    a := m;
  end;
  // добавляет незначащие нули
  for i := 1 to 8 - Length(Result) do
    Result := '0' + Result;
end;

// перевод 6-разрядного числа из двоичной системы в десятичную
// двоичное число подставляется в виде строки символов
function FromBin(s: string): Byte;
var
  i, e: Byte;
begin
  Result := 0;
  for i := 1 to 6 do
  begin
    e := 1 shl (6 - i);
    if s[i] = '1' then
      Result := Result + e;
  end;
end;

// непосредственно кодирование
type
  TCoola = array[1..1] of Byte;
  PCoola = ^TCoola;

procedure TForm1.Button1Click(Sender: TObject);
var
  inf: file of Byte;
  ouf: TextFile;
  uue: PCoola;
  b: array[1..4] of Byte;
  bin, t: string;
  szf, oum, szl, szh, sxl, sxh, i, j: LongInt;
begin
{$I-}
  AssignFile(inf, Edit1.Text); // входной файл
  Reset(inf);
  szf := FileSize(inf); // размер файла
  szh := (szf * 8) div 6; // размер результата
  if (szf * 8 - szh * 6) = 0 then
    szl := 0
  else
    szl := 1; //
  GetMem(uue, szh + szl); // выделение памяти
  oum := 1;
  while not (Eof(inf)) do
  begin
    b[1] := 0;
    b[2] := 0;
    b[3] := 0;
    b[4] := 0;
    // чтение должно быть сделано посложнее,
    // дабы избежать "read beyond end of file"
    Read(inf, b[1], b[2], b[3]);
    // читаем 3 байта из входного файла
    // и формируем "двоичную" строку
    bin := ToAnySys(b[1], 2) + ToAnySys(b[2], 2) + ToAnySys(b[3], 2);
    // разбиваем строку на куски по 6 бит и добавляем 32
    t := Mid(bin, 19, 24);
    b[4] := FromBin(t) + 32;
    t := Mid(bin, 13, 18);
    b[3] := FromBin(t) + 32;
    t := Mid(bin, 07, 12);
    b[2] := FromBin(t) + 32;
    t := Mid(bin, 01, 06);
    b[1] := FromBin(t) + 32;
    // запихиваем полученные байты во временный массив
    uue[oum] := b[1];
    oum := oum + 1;
    uue[oum] := b[2];
    oum := oum + 1;
    uue[oum] := b[3];
    oum := oum + 1;
    uue[oum] := b[4];
    oum := oum + 1;
  end;
  // входной файл больше не нужен - закрываем его
  CloseFile(inf);
  // формируем выходной файл
  AssignFile(ouf, Edit2.Text); // выходной файл
  Rewrite(ouf);
  oum := 1;
  sxh := (szh + szl) div 60; // число строк в UUE файле
  sxl := (szh + szl) - sxh * 60;
  // заголовок UUE-файла
  Writeln(ouf, 'begin 644 ' + ExtractFileName(Edit1.Text));
  // записываем строки в файл
  for i := 1 to sxh do
  begin
    Write(ouf, 'M');
    // 'M' значит, что в строке 60 символов
    for j := 1 to 60 do
    begin
      Write(ouf, chr(uue[oum]));
      oum := oum + 1;
    end;
    WriteLn(ouf);
  end;
  // записываем последнюю строку, которая
  // обычно короче 60 символов
  sxh := (sxl * 6) div 8;
  Write(ouf, chr(sxh + 32));
  for i := 1 to sxl do
  begin
    Write(ouf, chr(uue[oum]));
    oum := oum + 1;
  end;
  // "добиваем" строку незначащими символами
  for i := sxl + 1 to 60 do
    Write(ouf, '`');
  // записываем последние строки файла
  WriteLn(ouf);
  WriteLn(ouf, '`');
  WriteLn(ouf, 'end');
  CloseFile(ouf);
  FreeMem(uue, szh + szl); // освобождаем память
  ShowMessage('DONE.'); // Готово. Забирайте!
end;

end.


1) Читаем из исходного файла 3 байта.
2) Разбиваем полученные 24 бита (8x3=24) на 4 части, т.е. по 6 бит.
3) Добавляем к каждой части число 32 (десятичное)

Примеp: Имеем три числа 234 12 76.

Побитово будет так -   11101010 00001100 01001100
разбиваем и полyчаем - 111010  100000  110001  001100 
добавляем 32 -        +100000 +100000 +100000 +100000
                       ------  ------  ------  ------
                      1011010 1000000 1010001  101100
или в буквах -            Z       @       Q       ,


Вот собственно и все. В UUE файле в пеpвой позиции стоит кол-во закодированных символов + 32. Т.е. вся стpока содержит 61 символ. 1 символ идет на кол-во. Остается 60 символов кода. Если подсчитать, то мы yвидим, что для получения 60 символов кода необходимо 45 исходных символов. Для полной строки в начале стоит буква "M", а ее ASCII код = 77. 45+32=77.


Author: Sergei Dubarev
Source: http://delphiworld.narod.ru
ID: 04025



В связи с бурным развитием электронной почты встала проблема передачи бинарных файлов в письмах. Существующая технология не позволяет передавать такие файлы напрямую т.к. в них содержатся символы с кодами менее 32 и более 127 которые воспринимаются программным обеспечением как управляющие.

Для решения этой проблемы был разработан метод UU(E)-кодирования. Суть метода заключается в разбиении трех восьмибитовых слов (24 бита) на четыре шестибитовых, добавляя к каждому слову число 32 (код пробела), чтобы получить возможность пеpедать это в обычном письме электронной почты. Таким обpазом, шестибитное слово преобразуется к набору

`!"#$%&'()*+,-./012356789:;<=>?@ABC...XYZ[\]^_,

доступному для передачи.

Во избежании потерь, пробелы не используются в выходном UU-коде, а заменяются на символ с кодом 96 - обратная кавычка.

Перевод текста в UUE:

Исходный текст : M        o        d
Номера по ASCII: 77       111      100
По словам(8bit): 01001101 01101111 01100100

По словам(6bit): 010011 010110 111101 100100
Номера по ASCII: 19     22     61     36
                 Прибавляем код пробела (32 по ASCII)
Номера по ASCII: 51     54     93     68
Текст UUE      : 3      6      ]       D

Итог           : Mod > 36]D

Другой, менее популярный метод, называется XX-кодированием, и отличается от UU только набором символов - здесь используются: +-01..89ABC...XYZabc...xyz. С одной стороны метод XXE удобнее, так как использует больше "обычных символов", и имеет меньшую вероятность повреждения - некоторые символы UUE не конвертируются нормально из EBCDIC в ASCII и наоборот. С другой стороны в наборе символов UUE нет "маленьких" букв, хотя сейчас оба регистра сим волов проходят через средства коммуникаций без проблем.

В общем случае готовый UUE файл выглядит так:

[ section a of b of file filename.ext  < uuencode by Dos Navigator > ]
[ filetime xxxxxxxx ]
[ begin 644 filename.ext ]
[ UUE-код ]
[ end ]
[ CRC областей ]

Необязательные параметры заключены в квадратные скобки.

Рассмотрим назначение этих параметров подробнее.

  • Поле section предназначено для отделения секций UUE-кода и информирует о номере текущей секции и общем количестве секций.
  • Поле filetime предназначено для сохранения и последующего восстановления при декодировании времени создания файла и представляет собой упакованный формат DOS.
  • Поле begin отделяет начало UUE-кода и несет информацию об имени декодируемого файла. Число 644 не является волшебным - он о несет в себе атрибуты файла в стандарте unix и игнорируется в DOS-системах.
  • После begin идет собственно UUE-код который представляет собой набор UUE-символов, причем первым символом идет количество байт, закодированных в этой стpоке. Обычно это "M" - 45'й символ в таблице кодировки UUE - так как во всех строках, за исключением последней, передается по 45 восьмибитовых слов, закодироваенные в 60 шестибитовых (8*45 = 6*60 = 360).
  • Конец UUE-кода обозначается директивой end.
  • Область CRC содержит контрольные суммы секций и файла в целом.


Как вычисляется CRC.[править | править код]

Размер CRC - 16 бит. Для каждого последующего байта с точки зрения языка Ассемблера она вычисляется так:

     ror     [word ptr ChkSum],1
     movzx   ax,[byte ptr CurrentByte]
     add     [word ptr ChkSum],ax

Пеpед началом подсчета [ChkSum] должен быть равен нулю. По окончании подсчета контрольная сумма UUE и равна [ChkSum]. Таким образом видно, что ChkSum файла любой длины, состоящего из одних нулей будет нуль. Далее следует небольшой примеp на языке Pascal, вычисляющий контрольную сумму of 'entire input file'.

uses
  Dos;

const
  BufSize  = 16*1024;

var
  f        : File;
  ChkSum   : Word;
  FSize    : LongInt;
  Buf      : Array[1..BufSize] of Byte;
  i        : Word;
  FName    : PathStr;

procedure CalcChkSum(var Buf; Size: Word; var PrevSum: Word); assembler;
asm
      mov     cx,Size
      jcxz    @@End
      push    ds
      lds     si,Buf
      les     di,PrevSum
      mov     dx,word ptr [es:di]
      xor     ax,ax
    @@1:
      lodsb
      ror     dx,1
      add     dx,ax
      loop    @@1
      pop     ds
      mov     word ptr [es:di],dx
    @@End:
end;

begin
  if ParamCount <> 1 then
    Exit;
  FName := ParamStr(1);
  WriteLn('Calculating UUE CheckSum of "' + FName + '"...');
  FileMode := 0;
  Assign(f, FName);
  Reset(f, 1);
  FSize := FileSize(f);
  ChkSum := 0;
  for i := 1 to FSize div BufSize do
  begin
    BlockRead(f, Buf, BufSize);
    CalcChkSum(Buf, BufSize, ChkSum);
  end;
  i := FSize mod BufSize;
  if i > 0 then
  begin
    BlockRead(f, Buf, i);
    CalcChkSum(Buf, i, ChkSum);
  end;
  WriteLn('sum -r/size ', ChkSum, '/', FSize, ' entire input file');
  Close(f);
end.


Следует учесть, что контрольная сумма каждой отдельной секции (from "begin"/first to "end"/last encoded line) вычисляется с учетом того, что каждая стpока оканчивается на ASCII символ 0Ah. Корни этого растут из того, что UUE был первоначально предназначен для UNIX-систем. Таким обpазом контрольная сумма для стpочки 'end' должна вычисляться как для 'end'#$0A (в паскалевском варианте).


Source: http://algolist.manual.ru
ID: 04026