шифра. Другой вариант – переписать сообщение, используя, например, синонимы, либо удалив часть сообщения, которую легко восстановить из контекста, таким образом, чтобы длина текста стала кратной периоду.
Теперь перейдем к рассмотрению исходного кода нашего приложения. Как и в прошлый раз, начнем с объявления класса необходимых нам типов, а также класса формы. Соответствующий программный код показан в листинге 12.9. Здесь мы ввели целочисленную константу, ограничивающую длину задаваемого периода. В данном случае она равна 100. Нам понадобится помнить саму перестановку, при помощи которой будет осуществляться транспозиция сообщения, поэтому вводится соответствующий тип.Листинг 12.9.
Объявление типов и класса нашей формы
const
MaxTerm = 100;
type
TRearrangement = array [0..MaxTerm] of Integer;
TfmTransposition = class (TForm)
mmDecryptMessage: TMemo;
mmEncryptMessage: TMemo;
lbDecryptMessage: TLabel;
lbEncryptMessage: TLabel;
lbRearrangement: TLabel;
edRearrangement: TEdit;
btnEncryptMessage: TButton;
btnDecpyptMessage: TButton;
procedure btnEncryptMessageClick(Sender: TObject);
procedure btnDecpyptMessageClick(Sender: TObject);
private
{ Private declarations }
Rear: TRearrangement;
function RecalcRearrangement(nKey: Integer): Boolean;
function GetLine (Lines: TStrings;
nRow, nInd: Integer): String;
procedure EncryptDecrypt(SrcLines, DstLines: TStrings;
nKey: Integer);
public
{ Public declarations }
end;
Теперь перейдем к рассмотрению исходного кода решаемых в данном случае подзадач. Первой функцией, с которой мы начнем, будет функция разбора введенной строки, выделяющая перестановку из нее и проверяющая, является ли она допустимой.
Функция RecalcRearrangement подготавливает перестановку требуемым образом для шифрования либо дешифрования в зависимости от параметра пКеу, который принимает два значения: 0 и 1. Значение 0 указывает на то, что будет производиться шифрование сообщения и дополнительных действий по подготовке перестановки не требуется, за исключением проверки ее корректности. Значение 1, напротив, указывает на то, что будет производиться дешифрование сообщения и требуется еще дополнительно преобразовать перестановку так, чтобы она была симметрична исходной, в этом случае процесс дешифрования ничем не будет отличаться от процесса шифрования.
Чтобы введенная перестановка считалась корректной, необходимо и достаточно выполнить три следующих требования:
• введены только числа через пробел;
• все числа не повторяются;
• числа находятся в диапазоне от 1 до их общего количества.
Проверка первого условия осуществляется следующим образом. Изначально считается, что в строке идут пробелы. Как только пробелы заканчиваются, предполагается, что началось число, и до тех пор, пока мы опять не встретим пробел, выделяем это число. Как только встретили пробел, пытаемся преобразовать выделенную часть из строкового представления в численное. После этого добавляем полученное число к итоговой перестановке. Когда фрагмент кода, в котором находится первый цикл с условием после него, отработает, в массиве Rear будет храниться введенная перестановка (в Rear [0] хранится количество чисел в полученной перестановке). Сразу за первой проверкой осуществляется совместно вторая и третья, то есть проверяется допустимость самих введенных чисел, а также их уникальность. После всех проверок при необходимости осуществляется преобразование исходной перестановки к симметричной.
Для получения симметричной перестановки стоит выполнить нехитрое действие по обмену местами индексов чисел и сами х чисел, то есть если имеется перестановка 3 1 2, то она преобразуется в 2 3 1, так как 1 стоит на втором месте, 2 – на 3,3 – на 1.
Исходный код данной функции приведен в листинге 12.10.
Листинг 12.10.
Функция разбора строки и проверки допустимости перестановки
function TfmTransposition.RecalcRearrangement(nKey: Integer):
Boolean;
var
i: Integer;
s: String;
Space: Boolean;
Used: array [1..MaxTerm] of Boolean;
ExRear: TRearrangement;
begin
Result := False;
Rear[0] := 0;
Space := True;
//выделяем каждое слово, разделенное пробелом,
//и преобразуем его к числу
for i := 1 to Length(edRearrangement.Text) do
if (edRearrangement.Text[i] = ' ') and (not Space) then
begin
Inc(Rear[0]);
Rear[Rear [0]] := StrToInt(s);
Space := True;
end
else
if (edRearrangement.Text[i] <> ' ') then
begin
if Space then
begin
Space := False;
s := '
end;
s := s + edRearrangement.Text[i];
end;
if not Space then
begin
Inc(Rear[0]);
Rear[Rear[0]] := StrToInt (s);
end;
//проверяем допустимость полученных чисел
FillChar(Used, SizeOf(Used), False);
for i := 1 to Rear[0] do
if (0 < Rear[i]) and (Rear[i] <= Rear[0])
and not Used[Rear[i]] then
Used[Rear[i]] := True
else
Exit;
//преобразуем перестановку к шифровке, обратной
//для симметричности процесса дешифровки
if nKey = 1 then
begin
ExRear[0] := Rear[0];
for i := 1 to Rear[0] do
ExRear[Rear[i]] := i;
Rear := ExRear;
end;
Result := Rear[0] > 1;
end;
Еще для упрощения алгоритма шифрования необходимо уметь получать часть текста заданной длины, начиная с указанной позиции, в виде одной строки, пропуская все переводы строк. Это действие выполняет следующая описываемая функция. Алгоритм ее работы довольно прост. Изначально в результирующей строке нет ни единого символа. Далее осуществляется двойной вложенный цикл. Цикл верхнего уровня осуществляет изменение значения переменной, начиная с указанной строки до самой последней. Вложенный цикл, в свою очередь, изменяет значение переменной, первый раз начиная с указанной позиции в строке, а в остальных случаях всегда с 1, до длины текущей обрабатываемой строки. Каждый очередной символ добавляется к результирующей строке до тех пор, пока не будет достигнута заданная длина строки, равная периоду транспозиции. Соответствующий код приведен в листинге 12.11.
Листинг 12.11. Функция получения части текста заданной длины, начиная с указанной позиции, в виде одной строки
function TfmTransposition.GetLine(Lines: TStrings;
nRow, nInd: Integer): String;
var
i, j, k: Integer;
s: String;
begin
Result := '
s := '
k := nInd;
for i := nRow to Lines.Count – 1 do
begin
for j := k to Length (Lines[i]) do
begin
s := s + Lines[i][j];
if Length(s) = Rear[0] then
begin
Result := s;
Exit;
end;
end;
k := 1;
end;
end;
Подготовительный этап мы рассмотрели, теперь остается рассмотреть основной код программы. Обработчики кнопок Onclick вызывают один и тот же метод и указывают необходимые параметры, чтобы зашифровать либо дешифровать текст сообщения. Процедура EncryptDecrypt в качестве параметров принимает источник текста сообщения, с которым нужно проделать необходимые действия, приемник преобразованного текста сообщения и тип преобразования. Последний параметр принимает одно из двух значений: 0 или 1. Значение О указывает на то, что будет производиться шифрование сообщения. Значение 1 указывает на то, что будет производиться дешифрование сообщения. Процедура EncryptDecrypt выполняет следующие действия. Сначала она пытается подготовить необходимую перестановку и, только если все прошло успешно, переходит к попытке преобразования текста сообщения, но предварительно делает еще одну проверку. Эта проверка заключается в следующем: нужно удостовериться в соответствии общей длины текста накладываемому на нее ограничению, то есть длина обязана быть кратна периоду транспозиции. Если все хорошо, то далее следует код преобразования текста сообщения с использованием подготовленной транспозиции. Для начала приведем исходный код, который находится в листинге 12.12.
Листинг 12.12. Шифрование/дешифрование текста сообщения
procedure TfmTransposition.btnEncryptMessageClick (Sender:
TObject);
begin
EncryptDecrypt (mmDecryptMessage.Lines,
mmEncryptMessage.Lines, 0);
end;
procedure TfmTransposition.btnDecpyptMessageClick(Sender:
TObject);
begin
EncryptDecrypt (mmEncryptMessage.Lines,
mmDecryptMessage.Lines, 1);
end;
procedure TfmTransposition.EncryptDecrypt(SrcLines,
DstLines: TStrings;
nKey: