Книга: Песни о Паскале

Глава 25 Текстовые файлы

Глава 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 – параметр процедуры. Воспользовавшись этой процедурой, напишите программу для распечатки строк файла в обратном порядке. Подсказка: предварительно посчитайте количество строк в файле.

Оглавление книги

Оглавление статьи/книги

Генерация: 0.639. Запросов К БД/Cache: 2 / 0
поделиться
Вверх Вниз