Рис.54 – Принцип шифрования Юлия Цезаря
Как расшифровать её? Очень просто – сдвинуть буквы в обратную сторону. Но надо знать количество сдвигов – это число называют ключом шифра (в примере на рисунке ключ шифра равен двум). Разумеется, что ключ шифра и метод шифрования знали лишь двое: получатель письма и сам Юлий Цезарь.
Пойдем и мы вслед римскому полководцу, – создадим программу для шифрования текстового файла и его расшифровки. Скажу прямо: задача непростая, а потому решать её будем в два этапа. Вначале освоим шифрование отдельной строки, а уж потом «замахнемся» на файл. Но начнем с шифрования отдельного символа.
Суть проблемы
Зашифровать строку – значит зашифровать каждый её символ. Будь у нас готовая функция шифрования символа, задача решалась бы вмиг. Так займемся ею и начнем с заголовка. Дадим нашей функции имя Encrypt – «шифровать», она должна принимать исходный символ и возвращать другой, зашифрованный. Значит, заголовок функции может быть таким:
function Encrypt (X: char): char;
Теперь сосредоточимся на теле функции и рассмотрим известные нам приёмы обработки. Один из них состоит в применении каскада условных операторов:
if X=’А’
then Crypt:=’В’
else if X=’Б’
then Crypt:=’Г’
else...
Насколько удачно это решение? Прикинем количество вложенных операторов в этой лесенке. В русском алфавите 33 буквы, если взять заглавные и строчные, то получится 66. А если надумаем шифровать ещё и латинские буквы, и цифры и знаки препинания, то наберется около двух сотен символов. Такая лесенка условных операторов растянется на несколько этажей!
Не прибегнуть ли к оператору выбора CASE? Тогда тело функции будет намного проще:
case X of
’А’: Crypt:=’В’;
’Б’: Crypt:=’Г’;
...
end;
Обратите внимание, что метками оператора CASE здесь служат символы, – скоро вы узнаете, почему такое возможно. Этот вариант очевидно лучше первого, хотя две сотни меток – тоже не подарок. Но главное неудобство в ином: при изменении ключа шифра придется переписать все ветви оператора CASE, а это, согласитесь, скучно. Не поискать ли иного решения, простого и гибкого?
О кодировании символов
Первые компьютеры принесли инженерам массу неудобств. Взять хотя бы ввод и вывод данных. Дисплеи, принтеры и звуковые карты – тогда никто не слышал о них! Результат размышлений цифрового «мозга» высвечивался лампочками на инженерной панели ЭВМ, и в эту двоичную «цветомузыку» был посвящён лишь узкий круг мудрецов. Со временем изобрели простые принтеры, способные печатать лишь цифры, а затем и более совершенные – для печати букв и других символов. Как действуют подобные устройства?
Процессор компьютера, как известно, оперирует с числами. А людям подавай то текст, то картинку. Как связать одно с другим? Здесь инженеры вспомнили об алфавите. Ведь буквы в нём упорядочены, а значит, каждой букве можно сопоставить число; например, букве «А» – один, «Б» – два и так далее. Такое сопоставление называют кодированием, оно и решает проблему представления символов. Намерившись напечатать некоторый символ, компьютер передает его код на принтер, а уж принтер знает, как поступить с этим числом. При вводе с клавиатуры происходит обратное преобразование: нажатие клавиши заставляет клавиатуру отправить в процессор код соответствующего символа.
Итак, символы внутри компьютера кодируются числами. Мы посчитали, что общее количество букв, цифр и других знаков составляет более двухсот. Инженеры не поскупились и отвели для кодирования символов 256 чисел – от 0 до 255 включительно. Почему именно 256, а не 300 или 500?
Дело в том, что в двоичной системе счисления 256 – это круглое число, оно равно двойке в восьмой степени (если вам знакомо слово «байт», то речь о нём). Так был создан «алфавит» для компьютеров, он включает буквы, цифры, знаки препинания и управляющие символы, – последние выполняют специальные действия с печатающим устройством, например, перевод на следующую строку.
Понятно, что можно придумать несметное количество вариантов кодирования символов. Желая добиться взаимопонимания между техническими устройствами разных изготовителей, инженеры договорились о единой системе кодирования. Теперь она известна под именем ASCII (читается «аски») – American Standard Code for Information Interchange – американский стандартный код для обмена информацией. Со временем этот стандарт стал международным. Ныне используют несколько стандартов кодирования, один из них (для MS-DOS) представлен в приложение И.
Все кодируемые символы разбиты на три группы. Первую составляют управляющие символы с кодами от 0 до 31. Их названия вам мало о чем скажут, обратите внимание лишь на символы с кодами 10 и 13, – они служат для разбивки текста на строки.
Вторую группу составляют символы с кодами от 32 до 127, – здесь собран весь латинский алфавит, цифры и основные знаки. Коды латинских букв следуют в порядке латинского алфавита, причем разница между кодами одноименных больших и маленьких букв составляет 32.
Наконец, в третьей группе собраны русские буквы, символы псевдографики (их применяют для рисования таблиц) и другие редко используемые знаки. Коды большинства русских букв тоже следуют в порядке русского алфавита, но некоторые выпадают из этой последовательности.
Заметьте также, что символы русского и латинского алфавитов со схожими начертаниями (такие, как «А», «В», «Р») представлены разными кодами!
Чудесные превращения
Итак, символы в компьютере представлены своими кодами, то есть числами. А с числами работать легко: для превращения кода одного символа в код другого надо лишь прибавить либо вычесть некоторое число – шифрующий ключ.
Но как превратить символ в число и наоборот, — число в символ? Ведь это данные разных типов. Паскаль поможет вам своими встроенными функциями. Преобразовать число в символ можно либо функцией CHR, которая изначально присутствовала в языке, либо её современным аналогом по имени CHAR, которым я иногда буду пользоваться в дальнейшем. Обе функции принимают число, а возвращают символ, вот их объявления:
function Chr(arg : integer) : char;
function Char(arg : integer) : char;
Случайно ли, что имя функции CHAR совпадает с именем типа данных? Нет, ведь на самом деле обе эти функции ничего не делают! Они лишь подсказывают компилятору, что число в скобках — это код символа. И все! С такими «ненастоящими» функциями мы ещё встретимся, их применяют для преобразования типов данных. Вот как можно напечатать нескольких символов с их кодами.
var n: integer;
begin
for n:=40 to 50 do
Writeln(n,' ', Char(n))
end.
Для обратного преобразования — символа в число — применяют другую «ненастоящую» функцию по имени ORD (от ORDER — «порядковый номер»). Cейчас компиляторы предлагают и её аналог по имени Byte. Функция действительно возвращает порядковый номер символа в таблице ASCII, вот её заголовок:
function Ord(arg : char) : integer;
Воспользовавшись ею, мы тоже можем напечатать символы с их кодами.
var с: char;
begin
for c:=’A’ to ’F’ do