Текст книги "Песни о Паскале (СИ)"
Автор книги: Олег Деревенец
Жанр:
Драматургия
сообщить о нарушении
Текущая страница: 8 (всего у книги 29 страниц)
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
Writeln(c,' ', Ord(c))
end.
Здесь счетчиком цикла FOR служит переменная символьного типа. Паскаль допускает это, поскольку «знает», что каждому символу соответствует некоторое число – его код.
Итак, научившись превращать числа в символы и наоборот, мы оказались в шаге от поставленной цели – шифрования символа.
Шифрование символа
Вернемся к функции шифрования Encrypt, теперь мы можем упростить её до предела.
function Encrypt(arg: char): char;
begin
Crypt:= Char(Ord(arg) + CKey);
end;
Здесь CKey – ключ шифра, хранящийся где-то в глобальной константе или переменной. Превратив символ arg в число, мы прибавляем к нему ключ, а полученную сумму вновь превращаем в символ.
Все хорошо, прекрасная маркиза, за исключеньем пустяка: сумма в скобках может оказаться больше 255, а символа с таким кодом не существует! Как тогда поступит функция Char? Она вернет символ, укоротив его код на 256. Например, функция Char(260) вернет символ с кодом 260–256=4. Устроит нас это? Никак нет, поскольку первые 32 символа таблицы (коды от 0 до 31) – это управляющие символы. К сожалению, такие символы нарушат структуру текстового файла, и редактор не сможет прочесть его.
Значит, при передаче суммы в функцию Char надо проверить, не превышает ли она 255? Если да, то обрубим ей «хвост» и сдвинем ещё на 32 позиции выше, чтобы попасть в область видимых символов с кодами от 32 и далее.
if X>255 then X:=X–256+32; { смещаем «хвост» – в начало видимых символов }
Так получаем окончательный вариант функции шифрования символа.
function Encrypt(arg: char): char;
var x: integer;
begin
x:= Ord(arg)+ CKey;
if x>255 then x:= x–256+32;
Crypt:= Char(x);
end;
Расшифровка символа
Понятно, что для расшифровки символа надо выполнить обратный сдвиг. После вычитания ключа проверим, не попадает ли полученная разность в область управляющих символов? Если попадает, поправим её, сместив в область видимых символов. Вот текст функции расшифровки Decrypt.
function Decrypt(arg: char): char;
var x: integer;
begin
x:= Ord(arg)– CKey;
if x<32 then x:= x+256–32;
Decrypt:= Char(x);
end;
Теперь все готово для построения программы шифрования и расшифровки строки «P_24_1».
{ P_24_1 – Шифрование строки}
const CKey = 2; { Ключ Цезаря }
{– Шифрование одного символа –}
function Encrypt(arg: char): char;
var x: integer;
begin
x:= Ord(arg)+ CKey;
if x>255 then x:= x–256+32;
Encrypt:= Char(x);
end;
{– Расшифровка одного символа –}
function Decrypt(arg: char): char;
var x: integer;
begin
x:= Ord(arg)– CKey;
if x<32 then x:= x+256–32;
Decrypt:= Char(x);
end;
{– Шифрование строки –}
procedure EncryptStr(var arg: string);
var k: integer;
begin
for k:=1 to Length(arg) do
arg[k]:= Encrypt(arg[k]);
end;
{– Расшифровка строки –}
procedure DecryptStr(var arg: string);
var k: integer;
begin
for k:=1 to Length(arg) do
arg[k]:= Decrypt(arg[k]);
end;
{– Главная программа –}
var S: string;
Oper: integer;
begin
repeat
Write('Введите строку: '); Readln(S);
Writeln('Укажите операцию: 1– шифровать,’+
’ 2– расшифровать,’+
’ Прочие – выход');
Readln(Oper);
case Oper of
1: EncryptStr(S);
2: DecryptStr(S);
else Break;
end;
Writeln(S); { печатаем результат }
until false;
end.
Программа нуждается лишь в кратких пояснениях. Глобальная константа CKey содержит ключ шифра. Если со временем захотите сменить его, достаточно будет изменить константу и заново откомпилировать программу. Далее следуют описания двух функций: Encrypt и Decrypt – для шифрования и расшифровки символа. Процедуры EncryptStr и DecryptStr шифруют и расшифровывают строки, передаваемые им по ссылке VAR. И, наконец, в главной программе организован цикл для ввода шифруемой строки и кода выполняемой операции (Oper).
Откиньтесь в кресле и полюбуйтесь простотой блоков, составляющих эту программу! А во что бы мы превратили её, свалив в кучу эти простые алгоритмы? В заключение приведу протокол шифрования: пользователь ввел слово «pascal» и зашифровал его, получив слово «rcuecn». Затем ввел строку «rcuecn» и расшифровал её, получив назад данное мною слово.
Введите строку: pascal
Операции: 1 – шифровать, 2 – расшифровать, прочие – выход
Введите операцию: 1
rcuecn
Введите строку: rcuecn
Операции: 1 – шифровать, 2 – расшифровать, прочие – выход
Введите операцию: 2
pascal
Операции: 1 – шифровать, 2 – расшифровать, прочие – выход
Введите операцию: 3
Вряд ли я удержу вас от испытания столь полезного изделия. Во избежание ошибок, шифруйте строки небольшой длины. Рекомендую также хотя бы разок пройти программу по шагам.
Итоги
• В памяти компьютера символы представлены своими кодами – числами.
• Общее количество символов составляет 256, из них первые 32 – это управляющие, а остальные – видимые символы.
• Для преобразования числового кода в символ применяют функцию Char. Для обратного превращения – символа в число – пользуются функцией Ord..
• Паскаль «знает» о том, что символы кодируются числами, поэтому в счетчике цикла FOR допустимы символьные переменные, а в метках оператора CASE – символьные константы.
А слабо?
А) Измените программу шифрования с тем, чтобы ключ задавать с клавиатуры и передавать в процедуры и функции через параметр. Заголовки процедур и функций сделайте такими:
function Encrypt(arg: char; key: integer): char;
procedure EncryptStr(var arg: string; key: integer);
Хотя здесь проще держать ключ в глобальной переменной, но крупные программы этот приём запутывает, – там лучше передавать данные через параметры.
Б) Предположим, вы пятикратно зашифровали строку. Можно ли расшифровать её? И как это сделать?
В) Для введенной пользователем строки напечатать позиции всех входящих в неё символов (кроме пробелов) в алфавитном порядке. Для символов, которые встречаются несколько раз, напечатать их позиции в одной строке. Например, для слова «PASCAL»:
A – 2 5
C – 4
L – 6
p – 1
S – 3
Г) Для введенной пользователем строки напечатать позиции всех встречающихся в ней символов, кроме пробелов, в порядке их следования в строке. Например, для слова «PASCAL»:
P – 1
A – 2 5
S – 3
C – 4
L – 6
Д) Строки текстовых файлов порой содержат управляющие символы, например символ горизонтальной табуляции (код 9). Шифрование этих символов нашей программой нарушит структуру файла. Исправьте функции Encrypt и Decrypt так, чтобы они не изменяли символы, коды которых меньше 32.
Глава 25
Текстовые файлы
Мы мастерим программу шифрования текста. Шифрование отдельной строки освоено нами в предыдущей главе. Теперь научимся читать строки из одного файла и записывать их в другой.
Файлы хорошие и разные
Файлы – это хранилища данных, там может быть все что угодно: музыка, фильмы, книги. Ясно, что эта информация как-то закодирована, то есть, представлена в виде чисел – байтов. Файл любого типа – это набор байтов, хранящийся на диске (говорим пока о дисковых файлах). Каждому типу файлов нужен свой подход: к файлу нужна программа, «понимающая» его содержимое. Вам угодно слушать музыку? – к вашим услугам медиа-проигрыватель. Или надо печатать текст? – тогда запустите редактор текста. Но не наоборот! А все потому, что каждый тип файлов обладает структурой, понятной лишь соответствующей программе. Таким образом, файл и программа для работы с ним составляют логическое единство, – одно без другого лишено смысла.
Стало быть, структура или формат файла – его важнейшая характеристика. Все файловые форматы можно разделить на две категории:
• текстовые файлы;
• все прочие файлы, – их называют двоичными или бинарными.
О формате файла можно судить по его расширению. К текстовым относятся файлы с расширениями TXT – текст, BAT – пакетный файл, LOG – файл протокола и многие другие. Файлы наших программ с расширением PAS – тоже текстовые. А вот документы в формате Word (с расширением DOC) обладают сложной структурой, правильнее отнести их к бинарным. Так же, как и книги PDF–формата. В отличие от DOC и PDF, текстовые файлы открываются простыми редакторами текста – вроде «Блокнота» или редактора нашей IDE, который тоже работает с текстовыми файлами.
Формат текстовых файлов
Итак, любой файл – это набор байтов, записанных на диске. Как же расположены байты в текстовых файлах? – мы должны это знать. Воспользуемся «волшебным микроскопом» и рассмотрим через него отдельные байты небольшого текстового файла, составленного из четырех строк: в первой помещены три символа «1», во второй – два символа «2», третья строка пуста, а четвертая содержит символ «3».
Примечание. Вы можете исследовать текстовый файл в HEX–режиме просмотра такими программами, как Far, Total Commander и им подобными.
111
22
3
Наш воображаемый микроскоп изобразит этот файл цепочкой чисел (здесь показаны десятичные числа, хотя в HEX-режиме видны шестнадцатеричные).
49 49 49 13 10 50 50 13 10 13 10 51 13 10
Числа 49, 50 и 51 – это коды символов «1», «2» и «3» (по кодировке ASCII), а выделенные курсивом числа 13 и 10 – это парочка управляющих байтов, разбивающая файл на строки. Открыв такой файл редактором, мы не увидим управляющих байтов, но в файле они есть! Любая программа, работающая с текстовыми файлами, умеет находить эти ограничители строк при чтении текста и вставлять их в файл при записи в него.
История названий ограничителей исходит из глубины веков. Символ с кодом 13 назван Carriage Return – «возврат каретки» или сокращенно CR. Те, кто застал электрические пишущие машинки прошлого, помнят: перед печатью следующей строки, каретка такой машинки сдвигалась в крайнюю правую позицию, – это и есть возврат каретки.
А управляющий символ с кодом 10 назван Line Feed (LF) – «подача строки». Он заведовал подачей бумаги в продольном направлении с тем, чтобы следующая строка печаталась после предыдущей. Вот так и работал консольный интерфейс прошлого: барабанил буквочку за буквочкой, пока не получал управляющие коды CR и LF. Тогда каретка со скрежетом сдвигалась вправо, барабан, дёрнувшись, слегка смещал бумагу вперед, и печаталась следующая строка.
С годами формат текстовых файлов не изменился, и будет жить, пока существуют компьютеры. Секрет его живучести – в простоте и универсальности. В некоторых операционных системах текстовые файлы разбивают на строки не парой символов CR+LF, а лишь одним из них. Это по сути ничего не меняет, – файл по-прежнему являет последовательность строк-макаронин, нарубленных управляющими символами.
Доступ к текстовым файлам
В Паскале можно работать с файлами любых типов – и текстовыми, и бинарными. Сейчас нас интересуют только текстовые, о прочих пока умолчим.
Насколько сложно работать с текстовыми файлами? Расслабьтесь, – это совсем не больно! Вы уже работаете с ними, даже не подозревая об этом. Чтение и запись строк в текстовые файлы выполняется все теми же процедурами Readln и Writeln. Но с одним маленьким отличием: в первом параметре этих процедур дается ссылка на файловую переменную типа TEXT, которая должна быть объявлена в программе следующим образом:
var F: Text;
Тогда чтение и запись через текстовую переменную F выполняется так:
Readln (F, S); { Чтение одной строки файла в переменную S }
Writeln (F, ’Эта строка запишется в файл’);
Где тут сложности? Но пока неясно вот что:
• С каким именно файлом мы работаем? Ведь на диске их так много!
• В каком месте файла будет прочитана строка, и куда она будет помещена при записи?
Чтобы прояснить это, рассмотрим процесс чтения книги. Обычно я поступаю так:
1. Выбираю книгу на полке.
2. Открываю её в начале.
3. Читаю, пока не прочту или не усну.
4. В конце концов закрываю книгу и возвращаю на полку.
Точно так же – в четыре счета – обрабатывается файл. Далее в этой главе мы займемся чтением из файла, а запись в него рассмотрим в следующей главе.
Чтение из файла
Пусть нами объявлена файловая переменная F типа TEXT. Прежде чем воспользоваться ею для чтения некоторого файла, надо связать имя этого файла с файловой переменной. Это похоже на выбор книги для чтения – первый шаг в нашем списке. Связывание выполняют процедурой Assign – «назначить», в неё передают два параметра: файловую переменную и имя файла, например:
Assign(F, ’C:AUTOEXEC.BAT’);
Имя файла можно задать константой, переменной или их комбинацией – строковым выражением. Оно должно отвечать правилам, действующим в операционной системе. Указанный файл должен существовать, и система должна знать, где его найти. Впрочем, процедура Assign ничего не проверяет, она лишь помещает имя файла внутрь файловой переменной. И, если файла с указанным именем нет, процедура «не заметит» этого, но ошибка обнаружится на следующем шаге – при попытке открыть файл.
Второй шаг подготовки к чтению – открытие файла. Это вроде открытия книги на первой странице, оно выполняется процедурой Reset (что значит сброс или установка в исходное состояние). Этой процедуре нужен лишь один параметр – файловая переменная.
Reset(F);
Процедура Reset готовит файл к чтению, обращаясь при этом к операционной системе. Система выделяет память для работы с файлом, а также блокирует его, не давая другим программам удалить файл. После успешного открытия файловую переменную можно использовать далее в процедуре Readln так, как это было сказано выше. А если имя файла оказалось неверным или файл не существует? Тогда вызов процедуры Reset приведет к ошибке: программа сообщит: «File not found» – файл не найден, и аварийно прекратит работу.
После успешного открытия файла переходят к третьему этапу – собственно чтению из него (чтению книги). С этим вы уже знакомы, поскольку чтение выполняется известной процедурой Readln. Например, прочитать строку из файла можно так:
Readln(F, S);
Здесь S – это переменная строкового типа. Обратите внимание: в переменную S попадут только видимые символы строки, а управляющие коды – разделители строк – останутся «за бортом».
Но которая из строк файла будет прочитана? Первая, вторая или иная? При первом вызове после Reset процедура Readln прочтет первую строку файла, при втором – вторую и так далее. Если организовать цикл, то чтение продолжится вплоть до последней строки.
Применительно к чтению файлов говорят о позиции чтения, хотя увидеть эту позицию нельзя. Вызов процедуры Reset устанавливает эту воображаемую позицию в начало первой строки файла. Последующие вызовы процедуры Readln сдвигают её к началу очередной строки.
А что случится после чтения последней строки? Позиция достигнет конца файла, и очередной вызов процедуры Readln вызовет ошибку – событие крайне нежелательное. Чтобы избежать его, надо отслеживать достижение конца файла. Паскаль даёт для этого функцию по имени EOF, что означает End Of File – «конец файла». Булева функция EOF принимает один параметр – файловую переменную, и возвращает TRUE, когда позиция чтения «упирается» в конец файла.
if Eof(F)
then { достигнут конец файла }
else { можно продолжать чтение }
Как видите, функцией EOF нельзя определить позицию чтения (то есть, номер читаемой строки); она сообщает лишь о том, достигнут конец файла или нет.
Что делать с прочитанной книгой? – закрыть и вернуть на полку. Так же поступают и с файлом – закрывают его. Эта операция выполняется процедурой Close – «закрыть».
Close(F);
Закрытие файла освобождает память, выделенную для него операционной системой, и снимает блокировку, давая возможность другим программам делать с файлом все что угодно. Закрытие освобождает и саму файловую переменную, – теперь ею можно воспользоваться для доступа к другому файлу.
На рис. 55 показаны этапы чтения данных из файла.
Рис.55 – Четыре этапа чтения из файла
Последовательный доступ к файлу
Как видите, читать текстовый файл можно только последовательно, строку за строкой – от начала к концу файла, – нельзя читать строки в ином порядке. Поэтому текстовые файлы относят к файлам с последовательным доступом. В отличие от них, бинарные файлы (например, файлы баз данных) допускают произвольный доступ.
Впрочем, механизм последовательного доступа не запрещает программисту в любой момент вернуться к началу файла и повторить чтение – достаточно вызвать процедуру Reset.
Самореклама
Теперь испытаем то, что узнали о чтении текстовых файлов. Напишем небольшую программу, выводящую на экран свой собственный исходный текст, вот её первый вариант.
{ P_25_1 – распечатка текста программы }
var F: text; { файловая переменная }
S: string; { строка }
begin
Assign(F, 'P_25_1.pas'); { назначаем собственное имя }
Reset(F); { открываем файл для чтения }
repeat
if Eof(F) then Break; { прекратить, если конец файла }
Readln(F, S); { прочитать строку из файла }
Writeln(S); { вывести строку на экран }
until false;
Close(F); { закрываем файл }
Readln; { ждать Enter }
end.
Выделенный курсивом оператор проверяет достижение конца файла, и делает это перед чтением строки. Если же проверять в конце цикла
...
until Eof(F);
это неизбежно приведет к ошибке после чтения последней строки файла.
Цикл с проверкой в начале
Достижение конца файла надо проверять своевременно! Для этого в Паскале есть подходящий оператор цикла, – пора познакомиться с ним. До сих пор мы обходились двумя циклическими операторами, а именно:
• циклом с проверкой условия в конце REPEAT-UNTIL;
• циклом со счетчиком FOR-TO-DO.
Новый для нас оператор цикла строится из двух ключевых слов, вот его формат:
WHILE <условие> DO <оператор>
По-русски это читается так: "ПОКА условие истинно, ВЫПОЛНЯТЬ оператор такой-то". После ключевого слова DO допускается лишь один оператор, но на практике требуется больше. Потому здесь часто вставляют операторный блок BEGIN-END, в итоге получается такая конструкция.
WHILE <условие> DO BEGIN
<последовательность операторов>
END
Обратите внимание, что условия продолжения циклов в операторах WHILE-DO и REPEAT-UNTIL взаимно противоположны! Первый из них выполняется, пока условие истинно, а второй – пока оно ложно.
С новым оператором «самораспечатка» станет такой.
{ P_25_2 – распечатка текста программы }
var F: text; { файловая переменная }
S: string; { строковая переменная }
begin
Assign(F, 'P_25_2.pas'); { назначаем собственное имя }
Reset(F); { открываем файл для чтения }
while not Eof(F) do begin { пока не конец файла }
Readln(F, S); { прочитать строку из файла }
Writeln(S); { вывести строку на экран }
end;
Close(F); { закрываем файл }
Readln; { ждем нажатия Enter }
end.
В условии цикла WHILE видим отрицание NOT, значит, цикл будет выполняться, пока НЕ обнаружен конец файла. Проверьте работу этой программы. В следующей главе мы рассмотрим запись данных в текстовый файл и завершим наш шифровальный проект. А сейчас, как обычно, подведем итоги.
Итоги
• Текстовые файлы содержат строки видимых символов, отделенные друг от друга невидимыми на экране управляющими кодами CR (возврат каретки) и LF (перевод строки).
• К текстовым файлам обращаются через файловые переменные типа TEXT.
• Перед чтением файла нужны два шага: 1) связывание файловой переменной с именем файла процедурой Assign, и 2) открытие файла для чтения процедурой Reset.
• Для чтения отдельных строк вызывают процедуру Readln, при этом первым параметром процедуры указывают файловую переменную.
• После открытия файла его чтение начинается с первой строки; каждый вызов процедуры Readln смещает позицию чтения в начало следующей строки.
• Чтение файла возможно, пока не будет прочитана последняя строка. Попытка чтения за концом файла вызовет аварию программы.
• Чтобы узнать о достижении конца файла, вызывают функцию Eof, которая возвращает TRUE, если достигнут конец файла.
• Признак окончания файла исследуют в начале цикла, и для этого лучше подходит оператор цикла WHILE-DO.
• По окончании работы с файлом его закрывают процедурой Close.
А слабо?
А) Можно ли связать текстовую переменную F с файлом оператором присваивания?
F := ’c:autoexec.bat’;
Б) Напишите программу для вывода на экран файла, имя которого задается с клавиатуры.
В) Напишите три функции для подсчета:
• строк в файле;
• видимых символов в файле;
• всех символов файла (фактический объём файла).
Функции принимают один параметр – ссылку на файловую переменную. Напишите программу, определяющую упомянутые характеристики файла.
Г) Объявите две файловые переменные, свяжите их с одним и тем же файлом, а затем откройте через обе переменные. Вызовет ли это ошибку? Объясните результат, исходя из здравого смысла.
Д) Усовершенствуйте программу «вопрос-ответ» (глава 16) с тем, чтобы ответы хранились не в программе, а в отдельном текстовом файле. Тогда пользователи программы сами смогут сочинять ответы.
Е) Напишите процедуру для вывода на экран N–й строки файла, где N – параметр процедуры. Воспользовавшись этой процедурой, напишите программу для распечатки строк файла в обратном порядке. Подсказка: предварительно посчитайте количество строк в файле.
Глава 26
Я не читатель, – я писатель!
Наш шпионский проект по шифрованию файла подвигается к финишу. Ступим завершающий шаг: освоим запись в текстовый файл.
Запись в текстовый файл
Порядок записи в текстовый файл схож с его чтением, судите сами:
• для доступа к файлу используют файловую переменную типа TEXT;
• файловую переменную надо связать с файлом процедурой Assign;
• по окончании записи файл закрывают процедурой Close.
Вот схема, где отражены четыре этапа записи в файл (рис. 56).
Рис.56 – Четыре этапа записи в файл
Как видите, запись в файл отличается от чтения вторым и третьим этапами, ими и займемся.
Итак, после привязки файла процедурой Assign, файл открывают для записи процедурой Rewrite – «перезапись».
Rewrite(F);
Тут находим первое отличие: если для чтения требуемый файл должен существовать, то для записи этого не нужно. Если файла ещё нет, будет создан новый пустой файл. А при наличии файла он будет очищен, и вся информация в нём сотрется, как мел с доски. Будьте внимательны, иначе лишитесь нужной информации!
В открытый таким образом файл можно «печатать» нужные нам строки. Как? Вы уже знаете – процедурой Writeln. А что указать первым параметром? Правильно, – файловую переменную, вот так:
Writeln(F, S); { F – переменная типа TEXT, S – строковая }
Каждый вызов такой процедуры добавляет в конец файла очередную строку с разделителями. В отличие от чтения, где надо следить за достижением конца файла, при записи вы ограничены лишь объёмом винчестера и здравым смыслом (в большей степени последним).
По окончании записи файл закрывают все той же процедурой Close. Как и при чтении, закрытие файла освобождает выделенную для него память и снимает блокировку файла, позволяя другим программам работать с ним. К тому же закрытие файла гарантирует сохранение записанных данных на диске.
Пример записи в файл
Рассмотрим небольшой пример: заполнение файла числами от 1 до 10.
{ P_26_1 }
var F: text; { файловая переменная }
k: integer;
begin
Assign(F, 'P_26_1.txt'); { назначаем имя файла }
Rewrite(F); { открываем файл для записи }
for k:=1 to 10 do { записать 10 строк с числами }
Writeln(F, k);
Close(F); { закрываем файл }
end.
Есть вопросы? Запустите программу и проверьте, работает ли она. Запустили? Теперь отыщите в папке с программой файл «P_26_1.TXT» и откройте его любым редактором. Уверен, что вы обнаружите в нём столбик из десяти чисел.
Завершение шпионского проекта
Подойдя к финалу нашего проекта, мы научились: 1) шифровать отдельную строку, 2) читать строки из файла и 3) записывать строки в файл. Пора соединить все это: читая строки исходного файла, будем шифровать их, и записывать в другой файл, – так будет работать наша программа.
Прежде всего договоримся об именах файлов. Назначив зашифрованному файлу постоянное имя, например «CRYPT.TXT», мы избавим себя от ввода его имени с клавиатуры. Вводить мы будем либо имя исходного файла – при шифровании, либо имя конечного файла – при расшифровке. Обозначим эти неизвестные нам имена файлов как X1 и X2, и тогда схема обработки файлов будет такой.
Рис. 57 – Схема именования файлов при шифровании и расшифровке
С учетом этих договоренностей составим блок-схему программы (рис. 58).
Рис.58 – Блок-схема программы шифрования/расшифровки
Основную работу поручим процедуре шифрования-расшифровки, блок-схема которой показана на рис. 59. В неё передаём два параметра: имя обрабатываемого файла и код операции (зашифровать или расшифровать).
Рис.59 – Блок-схема процедуры шифрования-расшифровки
Теперь мы готовы смастерить шпионскую программу. Может быть, сами справитесь? По крайней мере, попытайтесь. Функции и процедуры шифрования символов и строк возьмите из программы «P_24_1». Написав свой вариант, сравните с представленным ниже.