Работа с файлами

В паскале работа с файлами осуществляется через специальные типы, доселе нам не известные. Это файловые типы, которые определяют тип файла, то есть фактически указывают его содержимое.

В прошлом выпуске мы с вами начали писать программу, которая и должна была стать основополагающей в наших дальнейших действиях. Программа называлась "Записная книжка", манипулировала с типом record и еще содержала маленькую ошибочку, которую, кстати, многие заметили.

Обратите внимание на этот код:

......
C := ReadKey;
case C of
'1'..'9': begin
Val(C, I, Code);
List_Item(I);
end;
'a': New_Item;
#27: Quit := true;
end;
......

В этом вся собака зарыта. Дело в том, что в условии упоминается о работе с 10-ю записями, а этот алгоритм позволяет использовать всего девять, так как вообще не умеет читать двухзначные числа. Как решить этот вопрос - Ваша задача! Программирование вообще без задач, которые нужно решать, не обходиться...

Ну а мы интенсивно двигаемся дальше. Сегодня - как и было обещано, файлы. Пока только теория, которую вам нужно усвоить (ее довольно много), практика будет в следующем выпуске.


Работа с файлами

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

При работе с файлами существует определенный порядок действий, которого необходимо придерживаться. Вот все эти действия:

  1. Создание (описание) файловой переменной;

  2. Связывание этой переменной с конкретным файлом на диске или с устройством ввода-вывода (экран, клавиатура, принтер и т.п.);

  3. Открытие файла для записи либо чтения;

  4. Действия с файлом: чтение либо запись;

  5. Закрытие файла.

Первое, на что хочу обратить внимание, это возможность связать файловую переменную не только с физическим файлом на носителе информации, но и с устройством. В качестве такового используются обычные псевдонимы устройств DOS. Вот основные два:

  1. CON - консоль (экран-клавиатура), то есть по записи в это устройство мы будем получать информацию на экран, при чтении информации из этого устройства, будем читать данные с клавиатуры.

  2. PRN - принтер. При записи в это устройство вы получите информацию на принтер.

Далее хочу обратить внимание на последний этап - закрытие файла. В принципе, не обязательное условие для файлов, из которых мы читаем данные. Если не закроем - ошибки это не вызовет, последствий тоже. Однако обязательно закрывать файл, если мы осуществляли в него запись. Дело в том, что если мы пишем данные в файл на диске и забываем его закрыть - информация не сохраниться. Она (информация) помещается во временный буфер, который запишется на диск только при закрытии файла.


Типы файловых переменных

Перед тем, как начинать работу с файлами, давайте посмотрим, какие существуют переменные для работы с ними. В Turbo Pascal имеется три типа таких переменных, которые определяют тип файла. Вот эти типы:

  1. Text

- текстовый файл. Из переменной такого типа мы сможем читать строки и символы.

  • File of _любой_тип_
  • - так называемые "типизированные" файлы, то есть файлы, имеющие тип. Этот тип определяет, какого рода информация содержится в файле и задается в параметре_любой_тип_. К примеру, если мы напишем так:

    F: File of Integer;

    То Паскаль будет считать, что файл F содержит числа типа Integer; Соответсвенно, читать из такого файла мы сможем только переменные типа Integer, ровно как и писать. Напишем так:

    type

    A = record

    I,J: Integer;

    S: String[20];

    end;

    var

    F: File of A;

    То получим очень интересную картину, когда файл содержит в качестве элементов записи. Мы сможем их читать, писать и производить с ними действия. Именно этим мы и займемся в процессе модификации нашей программы "Записная книжка".

  • File - нетипизированный файл. Когда мы указываем в качестве типа файла просто File, то есть без типа:

    F: File;

    То получаем "нетипизированный" файл, чтение и запись в который отличается от работы с файлами других типов. Эти действия производятся путем указания количества байт, которые нужно прочитать, а также указанием области памяти, в которую нужно прочитать эти данные. Это тема будущих выпусков.

  • Итак, разобрались с типами файлов. Теперь давайте по порядку разбирать действия, применяемые для работы с файлами. Я говорил о них выше.


    Связывание переменной с файлом

    Самый универсальный шаг. Выполняется одной и той же процедурой для всех типов файлов, а именно процедурой Assign:

    Assign(_переменная_файлового_типа_, 'путь к файлу');

    Как видите, в качестве параметров задаются переменная либого файлового типа и строка - путь к файлу, который, по правилам DOS, не может быть длиннее 79 символов. Вот пример использования assign:

    var
    T: Text;
    F1: File of Byte;
    F2: File;
    begin
    Assign(T, '1.txt');
    Assign(F1, 'C:ProgrammTp2.txt');
    Assign(F2, 'F:usrinapacheconfhttpd~1.con');
    ..........

    Как видите, это очень просто и сама процедура Assign проблем не вызывает. Давайте теперь рассмотрим следующий этап.


    Открытие файла

    Открытие файла - это уже более усложненный процесс, нежели связывание с ним переменной. Здесь учитывается, зачем открывается файл - для записи или чтения, а также в зависимости от типа файла процедуры выполняют различные действия.

    Тем не менее этот процесс не сложен и заключается в использовании одной из трех имеющихся процедур:

    1. Reset

    (_любая_файловая_переменная_);

    Открыват файл на чтение. В качестве параметра - файловая переменная любого из перечисленных выше типов. Это может быть текстовый, типизированный либо не типизированный файл. В случае с текстовым файлом, он открывается только на чтение. В случае с типизированным и нетипизированным файлом - он открывается на чтение и запись.

  • Append
  • (T: Text);

    Эта процедура открывает текстовый файл (только текстовый!) на запись. Выше я сказал, что Reset при задании параметра типа Text не позволит писать в него данные, открыв файл лишь для чтения. То есть если вы используете текстовый файл и хотите производить в него запись, нужнo использовать Append. Если чтение - Reset. В остальных случаях дело обходиться одной процедурой Reset.

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

  • ReWrite
  • (F) - создает новый файл либо перезаписывает существующий. Будьте осторожны, если не хотите случайно удалить нужный файл. Напомню, файл, открытый с помощью этой процедуры будет полностью перезаписан.

    В использовании процедур, думаю, проблем не будет. Однако возникает другой вопрос - что, если файла, который открывается, нет? Если он не существует на диске, то как мы сможем из него читать информацию? Программа выбьет ошибку в такой ситуации. Это реальная проблема, которая, кстати, очень просто решается.

    Способ проверки заключается в двух этапах: использовании ключей компилятора и функции IOResult, которая возвращает значение от только что выполненной операции ввода-вывода. С функцией разберемся быстро, а вот с такой штукой как ключи компилятора мы еще не сталкивались, поэтому остановимся подробнее.

    Ключи компилятора - это обыкновенные переключатели, которые контролируют ход выполнения программы исключая или включая реакцию на какие-нибудь условия. В нашем случае нас интересует условие, когда физически отсутствует нужный нам файл, либо не удалось открыть его по другим причинам. Ключей у Паскаля довольно много, мы пока изучим один, необходимый нам на данный момент.

    Оформляются ключи следующим образом: в скобках комментариев "{}" первым символом после открывающей скобки "{" ставиться знак доллара "$", после чего указывается имя ключа и его значение. Нас интересует ключ, который выключает вывод ошибок ввода-вывода, называется он "I". Выглядит все это следующим образом:

    {$I+} - включение вывода ошибок

    {$I-} - выключение вывода ошибок

    По сути дела отсутствие файла - это ошибка, которая возвращается функцией IOResult. Если же эта функция возвращает 0, то файл успешно открыт, без ошибок. Вот и вырисовывается последовательность действий, необходимых для проверки на наличие файла:

    1. Связываем переменную с файлом;

    2. Выключаем вывод ошибок на экран - {$I-}

    3. Открываем файл необходимой нам процедурой;

    4. Включаем вывод ошибок {$I+} - пусть будет для дальнейшего отслеживания таковых;

    5. Проверяем, если IOResult возвращает нуль, то все было путем и файл открыт. Иначе выводим ошибку.

    Вот пример такой программы:

    var
    T: Text;
    S: String;
    begin
    Write('Enter filename: ');
    Readln(S);
    Assign(T, S);
    {$I-}
    Reset(T); { открываем файл для чтения }
    {$I+}
    if IOResult <> 0 then { если не нуль, то была ошибка }
    begin
    Write('Error when open file!');
    Halt;
    end;
    { иначе все в порядке, продолжаем }
    ..........
    end.


    Закрытие файла

    Выше я говорил о том, зачем нужно закрывать файл и когда надо это делать. Закрытие файла производиться с помощью процедуры Close(F), где F - это переменная файлового типа. Эта процедура одна для всех типов файлов.


    Запись и чтение файлов. Часть I

    Сегодня я хочу рассказать о записи и чтении текствых и типизированных файлов, в следующем выпуске рассказ пойдет о чтении файлов без типа. Итак, переходим к непосредственной обработке файловой информации.

    Чтение файлов. Чтение файлов производится с помощью отлично известных нам процедур Read и Readln. Они используются также, как и при чтении информации с клавитуры. Отличие лишь в том, что перед переменной, в которую помещается считанное значение, указывается переменная файлового типа (дескриптор файла):

    Read(F, C);

    Здесь F - дескриптор файла, C - переменная (Char, String - для текстовых, любого типа - для типизированных файлов).

    Также сразу хочу упомянуть о одной, пожалуй самой главной функции при чтении файлов. Это функция поверки на конец файла - Eof(F): Boolean;. В качестве параметра - файловая переменная любого типа. Функция возвращает TRUE если достигнут конец файла и FALSE иначе. Здесь все очень просто, демонстрации ради давайте напишем небольшую программку. Пусть имеем текстовый файл. Давайте его распечатаем и заодно посчитаем, например, количество пробелов:

    var
    T: Text;
    С: Char;
    Spaces: Word;
    S: String[79]; { 79-макс. длина пути в DOS }
    begin
    Write('Enter filename: ');
    Readln(S);
    Assign(T, S);
    { открываем файл для чтения }
    {$I-}
    Reset(T);
    {$I+}
    { если не нуль, то была ошибка }
    if IOResult <> 0 then
    begin
    Write('Error when open file ', S, ' !');
    Halt;
    end;
    { иначе все в порядке, продолжаем }
    { ЦИКЛ: пока НЕ КОНЕЦ ФАЙЛА }
    While (not Eof(T)) do
    begin
    { читаем из файла переменную }
    Read(T, C);
    { если пробел, увеличиваем счетчик }
    If C = ' ' then Inc(Spaces);
    Write(C);
    end;
    Writeln('КОЛИЧЕСТВО ПРОБЕЛОВ: ', Spaces);
    Readln;
    end.

    Думаю, здесь все ясно. Продолжаем двигаться дальше и посмотрим, как производиться запись в файлы.

    Запись в файлы. Вы еще не догадались? Запись в файлы производиться точно так же, как и запись на экран - с помощью процедур Write и Writeln. Как и в случае с чтением, перед записываемой в файл переменной указывается тескриптор файла:

    Write(F, S);

    Здесь F - дескриптор, S - переменная.

    При этом, естественно, переменная должна соответствовать типу файла. Примера ради давайте составим еще одну небольшую программку, которая покажет работу с файлами. На сей раз уже используем типизированные файлы, а именно состоящие из чисел. Итак, мы имеем файл, в котором содержаться числа типа Integer. Давайте отсортируем эти числа в файле по возрастанию.

    План дейтсвий:

    • Отрываем типизированный файл из Integer; (проверяем на ошибку и т.п.)

    • Читаем все числа в массив (считываем, пока не конец файла)

    • Сортируем массив по возрастанию;

    • Записываем отсортированный массив обратно в файл.

    Получается такая программа:

    Program Sorting;
    uses Crt;
    var
    F: File of Integer;
    I, J, M: Word;
    Mas: Array[1..500] of Integer;
    S: String;
    begin
    ClrScr;
    Write('Enter filename: ');
    Readln(S);
    { открываем файл }
    Assign(F, S);
    {$I-}
    Reset(F);
    {$I+}
    if IOResult <> 0 then
    begin
    Write('Error when open file!');
    Halt;
    end;
    { пока не конец файла, читаем массив }
    While (not Eof(F)) do
    begin
    Inc(M);
    Read(F, Mas[M]);
    Write(Mas[M], ' ');
    end;
    { сортируем массив по возрастанию }
    For I := 1 to M do
    For j := 1 to M do
    if Mas[I] < Mas[J] then
    begin
    inc(mas[j], mas[i]);
    mas[i] := mas[j]-mas[i];
    dec(mas[j], mas[i]);
    end;
    Writeln; Writeln('=============================================');
    { перезаписываем файл }
    ReWrite(F);
    For I := 1 to 100 do
    begin
    Write(Mas[I], ' ');
    Write(F, Mas[i]);
    end;
    Writeln; Write('Elements in file: ', M);
    Close(F);
    Readln;
    end.

    Программа очень проста и хорошо демонстрирует работу с типизированными файлами. В качестве сортировки массива я использую метод пузырька, чтобы перезаписать файл использую ReWrite. Вроде не должно возникать никаких сложностей... Будут проблемы, пишите. Ну а на сегодня, пожалуй, все. Этой информации вам будет достаточно, в следующем выпуске займемся, как я уже сказал, нетипизированными файлами и продолжим писать программу "Записная книжка".



    Опубликовал admin
    16 Ноя, Воскресенье 2003г.



    Программирование для чайников.