На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Romtek, volvo877
  
> Работа с файлами , чтение, запись, удаление, переименование
    Работа с файлами это объемная тема. И совсем не просто охватить ее целиком и полностью в одном топике. Однако я попытаюсь.

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

    Паскаль умеет рассматривать информацию, находящуюся в файлах с таких точек зрения:
    • Текстовая информация - в файле находится (или будет находиться) текст. Под текстом я понимаю набор строк из символов. Каждая строка начинается с новой строки (простите за тавтологию).

      Текст не обязательно должен быть из букв. В нем могут содержаться и числа, и любые другие символы из таблицы ASCII, и на самом деле информацией в файле может быть совсем и не текст в привычном понимании этого слова. В файле может быть любая информация, но Паскаль может ее рассматривать (и, соответственно, имеет набор функций и процедур для ее обработки) как обычный текст.
    • Типизированная информация - в файле находится (или будет находиться) информация любого рода. Но структура такой информации обязательно должна повторяться. То есть, файл должен состоять из 1 и более одинаково устроенных частей. Например, файл может содержать различные строки из 20 символов. Тогда такой файл можно рассмотреть (и было бы лучше рассмотреть) как типизированный.

      Такое название (типизированный) файл получил из-за того, что в нем хранится однотипная информация. Типом данных для такого файла будет являться тот тип, какой имеет набор его частей. Проще говоря, если файл состоит из строк по 20 символов каждая, то типом информации в таком файле будет "строка в 20 символов". И такой файл можно называть типизированным файлом по типу - строка из 20 символов. Одна ячейка такого типа называется записью типизированного файла (или просто запись).

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

      При работе с типизированными файлами можно читать из них (или писать в них) только записи целиком. То есть, в примере с базой телефонов, нельзя работая с ней, как с типизированным файлом: вписать в него только имя, или только телефон. Можно либо прочесть, либо записать одну из записей одним махом. Но никак не по частям. Зато можно прочесть целую запись, изменить например имя, и потом записать ее назад.
    • Нетипизированная информация - файл содержит любую информацию, не поддающуюся какой либо классификации. Например набор личных данных, машинный код, закодированный или сжатый блок информации. При работе с таким файлом вся информация рассматривается как набор кусков по n байт, где n в диапазоне от 1 до 65535. Значение n определяется Вами, то есть Вы решаете, по сколько байт можно читать или писать в файл сразу. Содержимое этих кусков по n байт определяете тоже Вы для себя. Вы можете рассматривать содержимое как Вам будет удобно. То ли это часть слова, то ли часть внутреннего (машинного) представления числа, то ли символ, то ли массив.

    Итак, когда Вы определитесь, каким образом Вы собираетесь работать с файлом, тогда можно начинать работу.

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

    Есть три типа (в соответствии с тремя способами):
    • ExpandedWrap disabled
        <имя_переменной_дескриптора>: Text;
      - соответствует файлу с текстовой информацией
    • ExpandedWrap disabled
        <имя_переменной_дескриптора>: File of <любой_тип_данных_паскаля>;
      - соответствует типизированному файлу. В таком файле будут содержаться записи, тип которых будет определять параметр <любой_тип_данных_паскаля>. Как было сказано ранее, файл будет состоять из таких записей. Все они должны иметь один и тот же размер. Тип записи может быть любым - строка, символ, число, может быть составной сложный тип record. Тут следует сказать, что эти данные в файле должны храниться в машинном виде. То есть в том виде, в котором они находятся в памяти компьютера. При записи в типизированный файл данные автоматически конвертируются в такой вид и записываются. При чтении они автоматически загружаются в память в переменную, в которую мы читали. Например если файл состоит из чисел типа integer, то при записи в файл одной такой записи в нем добавится два байта - эти два байта и являются числом integer в машинном представлении. При чтении из файла такой записи в переменную типа integer данные должным образом перенесутся в эту переменную. можно попробовать записать в файл переменную типа word (которая тоже занимает 2 байта), а потом прочесть ее из файла как переменную типа integer, и это у нас получится !! Но при некоторых значениях этого числа данные будут искажены, так как у integer другой диапазон значений по сравнению с word. Есть исключения на тип данных записей, но они очевидные - например запись не может быть типа - file, или text, то есть файлового типа. Ведь файл не может состоять из файлов.
    • ExpandedWrap disabled
        <имя_переменной_дескриптора>: File;
      - соответствует нетипизированному файлу. У такого файла данные не имеют типа, поэтому при задании дескриптора файла он не указывается. Считается что одна запись - это 1 или более байт (сколько именно определяется позже).

    Когда мы опишем такой дескриптор, у нас появится переменная, которую мы сможем указывать при работе с файлом, и она будет однозначно определять конкретный файл.
    Вот примеры описания файловых дескрипторов:
    ExpandedWrap disabled
      type
          OnePhoneData=record         { Запись-описание абонента }
              Number:longint;         { Его телефон }
              Name:string[100];       { Его ФИО }
              Address:string[200];    { Его адрес }
          end;
              
      var
          TextFile:text;                      { Файл с текстовой информацией }
          PhoneFile: file of OnePhoneData;    { Файл с записями типа OnePhoneData }
          SomeData: file;                     { Файл с неопределённой информацией }
          Values: file of longint;            { Файл, состоящий из чисел (в их машинном представлении) }

    В этом примере описаны четыре различных файловых дескриптора. Каждый из них определяет различные типы информации, содержащиеся в файле.

    После того, как мы написали такие дескрипторы мы можем начинать работать с файлом.

    Для любого файла нужно выполнять последовательность действий в процессе работы. Вот эта последовательность:
    • Связать дескриптор и конкретный файл на компьютере
    • Открыть файл
    • Записать в файл или прочесть из него информацию
    • Закрыть файл
    Менять местами пункты нельзя. Рассмотрим каждый по очереди, но прежде я хочу сделать небольшое отступление по поводу работы с информацией в файле:

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

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




    1. Связь дескриптора и конкретного файла на компьютере


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

    Для дескрипторов любого типа связь с файлом осуществляется одной командой:

    ExpandedWrap disabled
      Assign(<имя_переменной_дескриптора>, <строка_имя_файла>);
    Параметры:
    <имя_переменной_дескриптора> - это, собственно, и есть дескриптор, который мы объявили ранее;
    <строка_имя_файла> - это либо константа, либо переменная-строка, содержащая в себе имя файла, с которым мы хотим осуществить связь. В этой строке может также указываться и путь к файлу. Если он не указан, то считается, что файл находится в той же папке, что и программа.

    Вот несколько разных примеров:
    ExpandedWrap disabled
      const
          filename1='myfile.txt';
      type
          Rec = record
              tel: longint;
              name: string;
              addr: string;
          end;
       
      var
          f:file of byte;
          tel:file of Rec;
          dat:text;
          s:string;
      begin
          s := 'd:\telbase';
          assign(f,'ByteData.dat');   { Теперь переменная f - это файл bytedata.dat, и состоит из байт }
          assign(tel,s+'\Phones.db'); { tel это файл с телефонами и обонентами. Находится в d:\teldata\phones.dat }
          assign(dat,filename1);      { dat - это текстовый файл myfile1.txt }
          ...                     {Тут идёт работа с файлами}
      end.

    Надеюсь у вас не возникло вопросов. Потому что мы переходим к следующей более сложной части:



    2. Открытие файла на чтение и/или запись

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

    Файлы нужно открывать для того, чтобы указать системе, что Вы в данный момент используете файл. И тогда другие программы (если у вас windows) или резиденты (если DOS) не смогут менять содержимое файла, так как после его открытия к содержимому файла имеете доступ только вы.

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

    Любая команда открытия файла открывает его, после чего в него можно писать и читать информацию. Но правда есть исключения - в некоторых случаях можно только читать, в некоторых только писать.

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

    Один и тот же файл нельзя открыть одновременно двумя разными способами.

    Первая команда:
    Rewrite
    Эта команда создает (если файла с таким именем еще нет) и открывает файл.
    Синтаксис:
    ExpandedWrap disabled
      Rewrite(<название_дескриптора_файла>[, <размер_записи>]);

    Как видно из формата, в этой команде следует указать дескриптор файла, который мы хотим открыть.
    Параметр <размер_записи> можно задавать только если дескриптором файла является дескриптор нетипизированного файла (то есть если дескриптор задан как f: file). В дескрипторе файла такого типа размер информации не указан. Но считается что файл состоит из байтов. Так вот этот параметр указывает, сколько байт можно прочесть либо записать в файл за один раз.

    Например: Если указать там цифру 10, то из нетипизированного файла командами чтения или записи будет читаться или записываться сразу по 10 байт. Меньше уже прочесть или записать за раз нельзя будет. Если нужно меньше - придется открывать файл заново. Если дескриптор файла является нетипизированным, и этот параметр не указан, то считается, что размер записи - 128 байт. Это значит, что читать или писать в этот файл можно только по 128 байт сразу. (Примечание: Для ТМТ паскаля размер записи если его не указывать будет 1 (один) байт. Хотя в справке указано 128. Это ошибка. Имейте в виду).

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

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

    Командой следует пользоваться для создания новых файлов, или если Вы хотите заново заполнить какой либо информацией старый файл.

    После открытия файла в файл можно записывать любую информацию. А так же ее читать (только если файл открыт как типизированный или нетипизированный (из текстовых файлов после открытия таким образом читать нельзя)). Вам может показаться абсурдной мысль о чтении информации из файла, открытого таким образом, Вы можете спросить: "Как же можно читать информацию, если файл становится чистым и пустым, и в нём уже нет ничего?"

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

    Следующая команда:
    Reset
    Эта команда открывает файл, связанный с дескриптором, и позволяет читать или писать в него информацию (на запись устанавливаются ограничения - см. далее). Файл должен уже существовать. Иначе может возникнуть ошибка (подробнее об отслеживании существования файла читайте ниже).
    Синтаксис:
    ExpandedWrap disabled
      Reset(<название_дескриптора_файла>[, <размер_записи>]);

    Как видите, полная аналогия с командой rewrite. Скажу вам сразу, что тут не будет ничего нового, так как в любой команде открытия файлов в паскале будет именно такой формат.

    Те же параметры: <название_дескриптора> - это дескриптор файла, который следует открыть для чтения.
    <размер_записи> - это размер записи для файлов, чей дескриптор описан как File (нетипизированный). Этот параметр подчиняется тем же правилам что и в команде rewrite.
    Позиция в файле открытом таким образом будет установлена на начало файла.
    Поэтому если мы открыли файл этой командой, то начнем читать всю информацию с самого начала (об этом часто забывают на первых парах. Например если мы сначала открыли файл командой reset, прочли часть информации, а затем переоткрыли его этой же командой, то мы при последующем чтении будем читать не информацию, идущую далее, а, опять же, с самого начала).
    Запись в файл, открытый таким образом можно производить только в том случае, если файл открыт как типизированный или нетипизированый. Записи в текстовый файл, открытый этой командой произвести нельзя.

    Теперь на очереди еше одна команда:
    Append
    Формат такой же как и у двух предыдущих команд с соблюдением всех правил и указаний.
    Открыв файл этой командой мы получаем возможность писать в него как и в команде reset. Разница между ними в двух вещах: после открытия файла таким образом читать информацию из него нельзя (есть исключения, читай далее), и второе - последующая запись в файл будет писать информацию после уже существующей в нем. Ну если конечно файл был пустым, то команда будет работать аналогично rewrite и reset.

    В процессе тестирования я выяснил, что эта команда в Borland Pascal'е к моему великому сожалению работает только с текстовыми файлами. Это значит что типизированные или нетипизированные файлы при помощи нее открыть нельзя. В отличие от этого паскаля компилятор ТМТ Pascal 3.9Lite позволяет открывать данной командой любой из трех типов файлов. Поэтому пользуясь компилятором ТМТ можно открыв файл таким образом производить как запись, так и чтение записей, перемещаясь по файлу (если файл не текстового типа) командой seek.

    Следует сказать, что файл должен существовать на носителе информации. В противном случае может возникнуть ошибка, а может и не возникнуть (читайте далее).

    Так-же есть команда, о которой я не могу не сказать, так как она очень тесно связана с открытием файлов.
    Truncate
    Синтаксис:
    ExpandedWrap disabled
      Truncate(<название_дескриптора_файла>);

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

    Теперь поговорим об очень важных директивах: {$I+} и {$I-}
    Про директивы паскаля можно почитать тут: Директивы транслятора BP7

    Эта директива включает или выключает автоматическую проверку ошибок ввода/вывода. Когда такая проверка включена, если возникает ошибка ввода/вывода, то выполнение программы завершается с сообщением о произошедшей ошибке. Но можно и отключить автоматическую проверку. И тогда на нас ляжет необходимость обрабатывать и корректировать соответствующие ошибки самим.

    Для чего нам это нужно? А очень просто. Типичной ошибкой ввода/вывода является то, что файл не найден. И если мы отключим автоматическое определение ошибок, то мы можем сами конкретно обработать вариант, когда файл, который мы пытаемся открыть не найден.

    Включить автоматическую проверку можно написав в коде программы так: {$I+}. Отключить, соответственно, можно так: {$I-}

    Если автоматическая проверка отключена, то результат операции ввода/вывода помещается в переменную IOResult (Input/Output), которая имеет тип Integer. Например при открытии файла туда помещается какое либо значение в зависимости от того, удалось ли открыть файл или нет.

    Если открытие произошло успешно, то в этой переменной будет находиться 0. Иначе в эту переменную будет записан код ошибки ввода/вывода, которая произошла. К сожалению я не нашел значений, и соответствующие им ошибки. Но нам этого и не нужно. Достаточно сравнить переменную IOResult с нулем после попытки открыть файл, чтобы узнать, открылся ли файл, или нет.
    Вот пример:
    ExpandedWrap disabled
      var
          f:text;
          name:string;
      begin
          write('Введите имя файла, который мы попытаемся открыть: ');
          readln(name);
          
          { В данный момент если файла нет, то ошибки не произойдет. Ведь файл еще не пытались открыть }
          assign(f, myname);
          
          {$I-} { - отключим стандартную проверку ошибок ввода/вывода}
              reset(f);   {попробуем открыть файл}
          {$I+}
          if ioresult = 0 then
              writeln('Все в порядке. Файл нашелся и открылся нормально')
          else writeln('Файл с именем ',myname,' не найден. Попробуйте другое имя');
              
          ... {ну тут остальные действия. Какие - читайте далее}
      end;


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


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

      В паскале есть два стандартных набора команд для работы с информацией в файлах.
      Это:
      1. Write и Writeln, Read и Readln
      2. BlockRead и BlockWrite
      То, как они работают целиком и полностью зависит от типа дескриптора файлов. Либо дескриптор файла текстовый, либо содержит типизированную информацию, либо нетипизированную. Для каждого из видов я опишу специфику работы этих команд.

      Начнём по порядку:
      1. Write и Writeln, Read и Readln
      1.1. Текстовые файлы


      В файлах, открытых как текстовые можно применять эти четыре команды с таким же результатом как и их применение для вывода (либо ввода) на экран. Применение команд Write и Writeln рассмотрено в статье нашего FAQ: Как вывести текст (строку) ?
      В синтаксисе следует учесть, что перед тем, как указывать что выводить, или куда вводить информацию нужно указать дескриптор файла, с которым мы работаем.
      Вот пример:
      ExpandedWrap disabled
        Writeln(f1,'Эта строка окажется внутри файла с дескриптором f1');

      Точно так же как и на экране эта команда занесет данную строку в файл, после чего перейдет на новую строку файла.
      При чтении из файла так же как и при чтении с клавиатуры следует соблюдать, чтобы тип информации совпадал с типом переменной, в которую мы хотим считать информацию.
      Например если в файле находится три строки, то прочесть их в переменную типа "число" скорее всего будет нельзя (если конечно в строках не содержатся числа).
      Например в файле такие данные:
      ExpandedWrap disabled
        Это строка
        12321
        Это было число
        А вот это будет три символа:
        №%@

      Можно прочесть такую информацию вот так:
      ExpandedWrap disabled
        var
          f:text;
          s1,s2,s3:string;
          v1:longint;
          c1,c2,c3:char;
        begin
          assign(f,<тут имя файла>);
          reset(f);
          readln(f,s1); {s1 <- "Это строка"}
          readln(f,v1); {v1 <- 12321}
          readln(f,s2); {s2 <- "Это было число"}
          readln(f,s3); {s3 <- "А вот это будет три символа:"}
          readln(f,c1,c2,c3); {c1 <- "№", c2 <- "%", c3 <- "@"}
          ...{остальные действия}
        end.

      При чтении как и с клавиатуры можно комбинировать.
      Например такая инфа
      ExpandedWrap disabled
        1234 Я -3.14
      может быть прочтена вот так:
      ExpandedWrap disabled
        var
          v:word;
          c:char;
          r:real;
        ...
          read(f1,v,c,r);
        ...

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

      Есть одна особенность при чтении из текстовых файлов:
      Признаком конца обычных файлов является достижение реального конца файла. Признаком достижения текстового файла могут быть две вещи: либо достижение реального конца файла, либо чтение из файла символа #26. Дело в том, что этот символ (стрелочка вправо) для текстовых файлов считается признаком конца файла. И если при чтении информации из файла (открытого как текстовый) встретится этот символ, то паскаль решит, что файл кончился, хотя на самом деле после этого символа может быть и остальная информация, и дальнейшее чтение уже не будет давать никакого результата. И даже более того: Если попытаться прочесть из файла этот символ не проверив перед этим, кончился ли файл, то это повлечет за собой такой глюк: в паскале прекратится выполнение каких либо действий с файлом, и вывод любой информации на экран. Следовательно старайтесь проверять перед чтением текстового файла, не кончился ли он.
      А что же делать, если нам нужно читать из файла текстовую информацию, включая и символ #26?
      определение символа #26 как конца файла происходит только в том случае, если данные из файла читаются в числовую переменную.
      к примеру в файле первый символ - #26
      ExpandedWrap disabled
        var
          v1:byte;
          v2:longint;
          v3:real;
          c:char;
        begin
          ...
          read(f,v1); или read(f,v2); или read(f,v3); {Даст глюк с концом файла}
          read(f,c); или read(f,s1); read(f,s2); {Глюка не будет. Символ #26 займет свое место}


      1.2. Типизированные файлы
      Это файлы с типизированным форматом данных.
      При объявлении дескриптора таких файлов указывается тип данных, содержащихся в файле.
      Для файлов такого типа возможно использование только команд read и write из данной категории. (ReadLn и WriteLn нельзя). Такое ограничение очевидно. Поскольку данные в файле это не строки текста, то нет и линий. А значит нельзя переходить по линиям.
      Команда Read читает одну или несколько записей из файла в переменную (или переменные). Тип переменных, в которые читается запись должен совпадать с типом типизированного дескриптора файла. Если вы объявили файл как file of char, то читать из файла в переменные типа string нельзя Можно только в char.
      Команда write напротив записывает в файл одну или несколько переменных. Так же как и в команде read типы переменных должны быть такими же как и тип данных файла.
      Данные в файле хранятся в внутреннем машинном виде. Поэтом раскрыв файл, в котором записаны переменные типа word (к примеру) вы не увидите привычных цифр. Вместо них в файле будет всякая абракадабра. Это и есть внутреннее представление переменных типа word. То, в каком виде они записаны в памяти. В данном примере на каждую переменную типа word будет отведено по два значка (байта). Количество байт, которое переменная занимает в памяти можно прочесть в хелпе по переменным паскаля, вызвав его из паскаля. Например введите слово Real, наведите на него курсор и нажмите Ctrl+F1. Вы увидите описание переменных с плавающей запятой. Там будет небольшая табличка для различных переменных, их максимальных и минимальных значений, а так же в столбце под названием Bytes указано количество байт, которое занимает каждый из типов таких чисел в памяти. Для числа Real это 6 и значит если файл будет описан как file of real, то каждая запись в файл одной переменной типа Real будет добавлять в него 6 байт, соответствующих значению этой переменной. При чтении такой записи из файла будет взято 6 байт, и занесены в то место памяти, где находится переменная, в которую мы читаем. Таким образом в этой переменной окажется то самое число типа Real.
      Теперь важная информация:
      Если текущим местом в файле не является конец, а к примеру середина, то с чтением у нас нет вопросов. При чтении мы будем читать текущую запись, следующую после нее, и так далее до конца файла. А что же будет если мы попробуем записать запись в файл? Если вы подумали, что информация после текущего места сдвинется дальше, чтобы дать место для записи, которую мы пытаемся записать, то вы ошибаетесь. Если попытаться записать запись не в конец файла, а в какое-либо другое место, то текущая запись будет затерта той, которую мы записываем.
      Поясню на примере.
      Допустим у нас такой файл:
      В файле в машинном виде содержится четыре числа типа Byte - 10 20 30 40
      На каждое из этих чисел в памяти приходится по одному байту. Поэтому в файле будет 4 байта.
      Допустим мы прочли из такого файла две переменные типа byte. Значит сейчас мы находимся на 3 переменной, но еще не читали ее.
      Если мы сейчас запишем в файл скажем вот такой командой: write(f,0) то в файле не станет 5 чисел. В нем их останется по прежнему 4, но третья текущая запись будет заменена новой, то есть числом типа byte равным нулю (как у нас написано). И в файле получится вот такая информация: 10 20 0 40 (Естественно в машинном виде) то есть тоже 4 байта, но один из них будет уже другим.

      Для того, чтобы вставлять в середину файла какую либо информацию сдвигая всю следующую дальше следует использовать более сложные уловки, о которых я (может быть) расскажу позже.
      Приведу пример:
      Создадим файл, в котором будет список студентов и результаты их зачетов по 5 предметам.
      И напишем программу, которая бы выводила эти данные.
      Вот программа, которая создает файл с описанием студентов:
      ExpandedWrap disabled
        type
          TStudentInfo=record
            name:string[30];
            kurs:string[20];
            ekz:array[1..5] of byte;
          end;
        var
          f:file of TStudentInfo;
          st:TStudentInfo;
          p:byte;
        begin
          assign(f,'students.dat');
          {$I-} {Будем сами отслеживать ошибки}
          reset(f); {Откроем файл. Позиция на данный момент в самом начале}
          {$I+}
          if ioresult<>0 then rewrite(f); {Если ошибка, занчит файла нет, и значит откоем его подругому}
          seek(f,filesize(f)); {Если мы открыли файл первым способом, то значит он уже был и значит там
                                есть записи. Следовательно  переместимся в его конец чтобы дописывать студентов}
          with st
          do repeat
            write('Введите имя студента (пустую строку для выхода): ');
            readln(name);
            if name='' then break;
            write('Введите курс:');
            readln(kurs);
            for p:=low(ekz) to high(ekz) do
            begin
              write('Введите оценку по экзамену №',p,': ');
              readln(ekz[p]);
            end;
            write(f,st); {Вот эта строка и записывает информацию о студенте в файл}
          until false;
          close(f); {Эту команду мы ещё не рассматривали, но об этом я расскажу в конце}
        end.


      А вот программа, которая читает и выводит эту информацию из файла:
      ExpandedWrap disabled
        type
          TStudentInfo=record
            name:string[30];
            kurs:string[20];
            ekz:array[1..5] of byte;
          end;
        var
          f:file of TStudentInfo;
          st:TStudentInfo;
          p:byte;
        begin
          assign(f,'students.dat');
          {$I-} {Будем сами отслеживать ошибки}
          reset(f); {Попробуем открыть файл}
          {$I+}
          if ioresult<>0 then
          begin
            writeln('Файл с данными не найден. Запустите сначала первую программу');
            exit; {Прервём выполнение программы}
          end;
          with st do
          repeat
            read(f,st); {Вот эта строка читает информацию о студенте из файла в st}
            writeln('Имя студента: ',name);
            write('Курс:',kurs);
            for p:=low(ekz) to high(ekz) do
              write('Оценка по экзамену №',p,': ',ekz[p]);
          until eof(f); {Эта команда ещё не рассмотрена, она будет возвращать неправду пока файл не кончится}
          close(f); {Эту команду мы ещё не рассматривали, но об этом я расскажу в конце}
        end.

      Поэкспериментируйте с программами, найдите их слабые места. Попробуйте от них избавитья %)

      1.3. Нетипизированные файлы
      С файлами такого рода нельзя использовать первую группу команд. То есть, команды write, writeln, read и readln. Такое ограничение происходит потому что не известен тип записи в файле, а значит и неизвестен тип переменной в которую нужно было бы указывать в этих командах.

      На самом деле я кривлю душой, говоря о том, что неизвестен тип, и поэтому нельзя использовать эти команды. Потому что в принципе тип - это байт, или несколько байт. И чтение либо запись в файлы такого типа запросто можно было бы осуществить. Но... но увы в паскале видимо программисты не додумались, либо не захотели думать об этом. Посему данное ограничение пусть остаётся на их совести.
        2. BlockRead и BlockWrite
        1.1. Текстовые файлы
        Нельзя использовать.

        1.2. Типизированые файлы
        Также нельзя использовать.

        1.3. Нетипизированые файлы
        Только файлы такого типа можно использовать с этими двумя командами.
        Команда BlockRead позволяет читать информацию из нетипизированного файла указанного размера. Вся прочтённая информация записывается в указанный буффер.

        Синтаксис:
        procedure BlockRead(<дескриптор_нетипизированного_файла>,<буффер>,<количество_записей>:word[,<результат>:word]);

        Рассмотрим параметры по порядку:
        • <дескриптор_нетипизированного_файла> - ну это ясно. Это дескриптор файла, из которго производится чтение.
        • <буффер>:longint - это любая переменная паскаля. Буффером может быть например массив. Или переменная типа "запись". Может быть строка, даже указатель. Паскаль не понимает, какой тип переменной в данном случае указан, и не производит перевода данных в соответствующий тип.
          Это значит, что если вы читаете этой командой из нетипизированного файла строку с текстом в текстовую переменную, то текст не будет преобразован в текстовую переменную. Читаемая информация будет занесена в то место памяти, которое занимает текстовая переменная без всякого соблюдения её внутреннего синтаксиса. Чаще всего в этом параметре указывается массив какой либо информации, в связи с тем, что эта команда позволяет читать множество записей сразу (смотри следующий параметр).
        • <количество_записей> - этот параметр задаёт, сколько записей за раз следует прочитать из файла данным вызовом этой команды. Если там указана цифра 3, то из файла прочтутся 3 записи. Напомню, что для нетипизированных файлов размер записи определяется в момент его открытия (это второй параметр команд открытия файла). Если запись указана как число 3 - то это 3 байта. И если параметр <количество_записей> в этом случае будет 10, то из файла будет прочтено 3*10=30 байт. Такой метод часто бывает удобен, если у вас в файле содержится набор одноразмерной информации. Допустим мы записали в файл массив из чисел типа longint, и сделали запись этих чисел в их внутреннем виде. На каждое число будет приходиться по 4 байта. Значит при открытии файла следует указать размер записи равным 4 байтам, а потом можно прочесть эти числа в массив, указав его имя в параметре <буффер>, и количество элементов, которое мы хотим прочесть из файла в параметре <количество_записей>. Данный параметр должен быть переменной или константой типа word.
        • <результат> - этот параметр является необязательным. Его можно не указывать. Если же указывать, то это должна быть переменная типа word. После выполнения процедуры BlockRead в этой переменной будет содержаться фактическое число прочтённых полных записей из файла. Дело в том, что при чтении из файла мы можем запросить прочесть больше записей, чем на самом деле осталось в файле. И тогда мы дойдём до конца, и далее читать уже будет нечего. Так вот этот параметр будет содержать количество записей, которое нам удалось прочесть. Считаются только полные записи, тоесть те, которые дочитались до конца (файл может окончиться где-то в середине очередной записи).

        После чтения очередной записи (или группы записей) команда BlockRead смещает указатель текущей позиции файла на количество_прочтённых_записей дальше.
        Суммарный размер информации, которую можно прочесть за раз этой командой не должен привышать 65535 байт, иначе возникнет ошибка. Это значит, что Размер_записи*<количество_записей> должно быть меньше чем 65536.

        Пример: В файле содержится 13 байт: 1112223334445
        Смотрите, что мы получим читая его.
        ExpandedWrap disabled
          var
            f:file; {Объявим дескриптор нетипизированного файла}
            b:array[1..100] of char;
            r:word;
          begin
            assign(f,'123.dat');
            reset(f,3); {Установим размер записи равным 3}
            blockread(f,d,2);
            {сейчас мы прочли две первые записи, тоесть 3*2=6 байт}
            {Эти 6 байт занеслись в первые 6 ячеек массива d - "111222"}
            blockread(f,d[7],10,r);
            {Теперь мы попытались прочесть ещё 10 записей из файла}
            {Но в файле осталось только 2 полные записи, и ещё одна неполная}
            {Поэтому переменная r будет содержать число 2}
            {А в массив начиная с 7 ячейки будут записаны ещё 7 байт}
            {Обратите на это внимание. Именно ещё 7 а не 6, как можно было бы предположить}
            ...


        Этой командой довольно удобно читать целые массивы каких либо типизированных данных.
        Нам нужно только знать, сколько байт в файле занимает информация об одном из типизированных данных.
        Помните раньше для типизированных файлов я давал пример - файл с данными о студентах и их оценках.
        Тип данных там был таким:
        ExpandedWrap disabled
          type
            TStudentInfo=record
              name:string[30];
              kurs:string[20];
              ekz:array[1..5] of byte;
            end;
        Так вот из такого файла командой BlockRead можно прочесть сразу (например) 10 записей.
        Пример:
        ExpandedWrap disabled
          type
            TStudentInfo=record
              name:string[30];
              kurs:string[20];
              ekz:array[1..5] of byte;
            end;
          var
            f:file;
            st:array[1..10] of TSudentInfo;
            res:word;
          begin
            assign(f,'students.dat');
            {$I-} {Будем сами отслеживать ошибки}
           
            {Попробуем открыть файл}
            {Обратите внимание - здесь принципиально важное место. Мы открываем файл
             с размером записи, равным размеру записи TStudentInfo, что позволит читать
             нам за раз одну запись}
            reset(f,sizeof(TStudentInfo));
            {$I+}
            if ioresult<>0 then
            begin
              writeln('Файл с данными не найден. Запустите сначала первую программу');
              exit; {Прервём выполнение программы}
            end;
            {Теперь прочтём сразу 10 описаний студентов в массив st}
            blockread(f,st,10,res); {Читаем 10*размер_записи_о_студенте байт}
            {После выполнения blockread в переменной res находится количество удачно прочтённых записей}
            {Поскольку в файле может быть меньше 10 описаний, для нас это принципиальные данные}
            writeln('Из файла удалось прочесть информацию о ,'res,' студенте(ах)');
            close(f); {Эту команду мы ещё не рассматривали, но об этом я расскажу в конце}
            {Ну а теперь можно вывести информацию о res прочтённых студентах на экран}
          end.



        Ну и наконец команда BlockWrite
        Синтаксис:
        procedure BlockWrite(<дескриптор_нетипизированного_файла>,<буффер>,<количество_записей>:word[,<результат>:word]);

        Эта команда имеет идиентичный синтаксис с командой BlockRead.
        Команда записывает данные из памяти указанной параметром <буффер> в файл, связанный с дескриптором <дескриптор_нетипизированного_файла>. Как и в команде BlockRead тут есть параметр <количество_записей> типа word, который указывает, сколько записей переписать из памяти в файл этой командой.
        Параметр <результат> после выполнения процедуры содержит количество удачно записанных записей. Это значит, что в процессе записи в файл носитель информации может быть переполнен, и дальнейшая запись будет невозможной. Так вот в этом параметре будет содержаться количество целых записей, которые удалось занести в файл до того, как заполнился носитель информации. Тут следует учесть, что носитель информации может заполниться на середине очередной записи. Недописаная запись не будет удалена из файла, однако в параметре <результат> она учтена не будет.
        Как и в команде BlockRead параметр <результат> не является обязательным, и его можно не указывать.

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

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

        Ну вот мы и добрались наконец к завершающей стадии работы с файлом.
        Это закрытие файла. После того, как информация, находящаяся в файле была должным образом обработана (или сформирована) файл обязательно следует закрыть. После закрытия файла все действия, проведённые нами с файлом вступят в силу. Не следует забывать этого действия, так как это может повлечь некоторые неприятные последствия. Во первых те изменения, которые вы поизвели в структуре файла могут не полностью сохраниться, и во вторых в системе файл останется открытым. А у каждой системы есть ограничение на возможное количество одновременно открытых файлов. Для ДОСа это 7 файлов (можно установить больше добавив в файл config.sys строку files=20). Для Windows больше, но всё же совершенно очевидно, что файл следует закрыть после работы с ним. Иначе после определённого количества запусков вашей программы в системе будет максимум открытых файлов, и в конце концов наступит момент, что система не сможет открыть ещё один.
        Файл любого типа закрывается командой Close, которая имеет простой синтаксис:

        procedure Close(<дескриптор_открытого_файла>);
        Попытка закрыть уже закрытый (либо ещё не открытый) файл не приводит ни к каким последствиям. Это бывает в некоторых случаях удобно.
          Далее нам следует рассмотреть другие функции, которые определены в Паскале для удобства работы с файлами.

          Это функции:
          Seek, FilePos, FileSize, Eof, SeekEof, EoLn, SeekEoLn, Delete, Rename. Вроде все.
          Будем расматривать по порядку.

          Seek

          Одна из наиболее часто используемых при работе с файлами комманда.
          Эта команда меняет в открытом файле текущее месторасположение.
          Работает только для файлов, открытых как типизированые или нетипизированные. Не работает в текстовых файлах. Файл должен быть открыт.
          Синтаксис:
          procedure Seek(<Имя_дескриптора_файла>,<порядковый_номер_записи>);

          Эта процедура перемещает текущее положение в файле в запись с номером <порядковый_номер_записи>. Положение в файле считается начиная с 0. Тоесть если мы хотим переместиться в начало файла f, нам следует написать Seek(f,0); Если хотим переместиться в 10 запись, то должны будем написать Seek(f,9); Когда файл типизированный, записью считается тип файла, когда файл нетипизированный, то размер записи в байтах указывается при открытии файла (второй параметр команды открытия файла).

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

          Можно конечно сделать так:
          1. Прочесть все записи из файла в массив.
          2. Пройтись по массиву, заменяя отрицательные записи на нули.
          3. Открыть этот же файл командой rewrite и записать заново все записи.

          Но вот вам куда более удобный способ:
          1. Читаем из файла текущую запись.
          2. Если эта запись меньше нуля, то перемещаемся на одну запись назад командой seek, и записываем в файл вместо отрицательной записи (которая у нас опять является текущей) запись с нулём.
          3. повторяем до самого конца файла эти действия.

          Такой подход даст значительно более быстро работающий результат.

          У вас может возникнуть один вопрос - откуда мы берём значение текущей записи, для того чтобы выполнить действие номер 2? Ведь нам чтобы сдвинуть номер записи назад, нужно знать номер текущей записи, иначе мы не сможем указать номер предыдущей записи. Можно конечно завести переменную, в которой содержать номер текущей записи, и изменять его в соответствии с алгоритмом. Однако это совершенно излишне, потому что именно для этого используется следующая команда.

          FilePos

          Эта команда используется для того чтобы узнать номер текущей записи в файле.
          Может быть использована только в типизированных и нетипизированных файлах. В текстовых файлах использовать нельзя (потому что в текстовых файлах нет понятия - запись).
          Записи в файле всегда нумеруются с нуля. Эта особенность очень часто у новичков вызывает ошибки.
          Допустим в файле всего находится 10 записей. Тогда первой записью всегда будет запись номер 0, а последней не 10, как можно было бы подумать, а 9. Тоесть записи будут нумероваться - 0, 1, 2...9 Всего получится 10 номеров (Вместе с 0). Учтите это и при использовании Seek.
          Синтаксис:
          function FilePos(<переменная_дескриптор_файла>):longint;
          Как видите эта команда - функция. Возвращаемым ею значением и будет текущее положение в файле.
          Тут также следует учесть, что текущим положением в файле будет номер текущей записи в файле, а не порядковый номер байта. Когда я начинал работать, я долго не мог понять разницы. Байты - это вся информация в файле. А записи - это блоки, из которых он состоит. Одна запись может быть размером к примеру 5 байт. И если эта команда вернёт цифру 10, то это будет значить, что сейчас в файле активна 11 запись (смотрите, мы учитываем ноль, поэтому 11). А это получится 11*5=55. Значит в файле эта запись будет начинаться с 55 байта (если их считать начиная с 1).
          Теперь немного практики:
          Файл состоит из записей типа String[100].
          Найдём и заменим в нём все записи, в которых встречается слово "ФИО" На наше ФИО.
          Вот код:
          ExpandedWrap disabled
            type
              sof100=string[100]; {Это тип информации в файле - обязательно нужно объявить (см. пояснение)}
            var
              f: File of sof100;
              s: sof100; {запись из файла будет здесь}
              fio: string; {Наше ФИО}
            begin
              write('Введём наше ФИО: ');
              readln(fio);
              if length(fio)>100 then fio:=copy(fio,1,100); {Обрежем лишние символы}
              assign(f,'fio.dat');
              reset(f);
              while not eof(f) do {Этого мы ещё не прошли - цикл будет выполняться пока не окончится файл}
              begin
                read(f,s);
                if s='ФИО' then {Если текущая запись - слово ФИО}
                begin
                  s:=fio; {Нужно преобразовать из строки в sof100, для этого присвоим s значение fio}
                  seek(f,filepos(f)-1); {переместим позицию файла в позицию на один меньше текущей}
                  write(f,s); {Перезапишем в файл наше ФИО}
                end;
              end;
              close(f);
            end.
          В данном примере мы описали специально тип информации, который содержится в файле:
          type sof100=string[100];
          Дело в том, что если тип информации в файле описать как string[100], то это будет работать нормально но(!) описав переменную s:string[100] мы не сможем ни записать её в файл, ни прочесть в неё из него. Это происходит из-за того, что Паскаль считает, что тип данных в файле (string[100]) это один тип данных, а тип переменной s - (тоже string[100]) это совсем другой тип данных. Тоесть для нас конечно же очевидно, что оба типа одинаковые, но для Паскаля это совсем не очевидно. Для него переменная s, и данные в файле f имеют различные типы, и следовательно в файл f нельзя будет записать такую переменную s. Поэтому нам необходимо объявить один общий тип - type sof100=string[100]; и создать типизированый файл такого типа, и переменную s тоже такого типа. Тогда паскаль понимает, что и то, и то имеют один тип.
          Конечно же эта функция может быть использована только на открытых файлах.

          FileSize

          Эта команда позволяет определить количество записей в файле. Тоесть это можно назвать размером файла (в записях). Работает только для типизированных или нетипизированных файлов (не работает для текстовых) и файл должен быть обязательно открыт.
          Синтаксис:
          function FileSize(<переменная_дескриптор_файла>):longint;
          Как и FilePos эта команда является фукнцией.
          Значение, которое она возвращает является количеством записей в файле на данный момент.
          Если в файле при открытии уже есть записи, то эта команда вернёт их количество. Если в течении работы с файлом это количество изменится (например мы добавим новые, или удалим старые) то при последующем вызове этой команды это изменение будет учтено. Поэтому можно сказать, что эта команда возвращает количество записей в файле на данный момент.
          Если при октрытии файла в нём находится информация не кратная размеру записей (ну например размер записи - 5, а в файле 27 байт - 5 записей и 2 байта), то функция вернёт количество полных записей в файле (тоесть в данном примере - 5).
          Количество записей считается с 1. Поэтому проблемм с нулями тут обычно не возникает (как в командах FilePos и Seek).

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

          Для этой команды я приведу переведённый пример из стандартного паскалевского хелпа:
          ExpandedWrap disabled
            var
              f: file of Byte;
              size : Longint;
            begin
              Assign(f, ParamStr(1)); {Имя файла узнаем из командной строки программы}
              Reset(f); {Откроем файл с размером записи - 1 байт}
              size := FileSize(f); {В переменной будет находиться текущий размер открытого файла}
              Writeln('Размер файла в байтах: ',size); {Выведем его}
              Writeln('Перейдём в середину файла...');
              Seek(f,size div 2); {Переходим}
              Writeln('Position is now ',FilePos(f)); {Выведем номер текущего положения в файле}
              Close(f); {Закроем файл}
            end.
          Я думаю, вопросов это не должно вызывать.

          EOF

          Это важная команда, которая повседневно применяется при работе с файлами (Я даже не смог обойтись без неё в предыдущих примерах, поэтому возможно вы уже догадались, что она делает).
          Расшифровывается так: End Of File - EOF (конец файла)
          При помощи этой команды можно узнать, достигли ли мы конца файла.
          Допустим мы должны вывести всю информацию из файла на экран. Мы будем читать её из файла пока он не кончится. Для того, чтобы определить - когда-же у нас кончился файл мы должны использовать эту команду.
          Синтаксис такой:
          function EOF(<переменная_дескриптор_файла>):boolean;
          Всё просто. Команда возвращает true, если файл кончился, и возвращает false, если ещё не кончился.
          Eof может использоваться для определения достижения конца любого файла: будь то типизированный файл, нетипизированный файл, или даже текстовый файл(учтите что и текстовый тоже).
          Команда не реагирует на символ #26 в текстовых файлах. Поэтому определить наличие этого символа при помощи Eof нельзя. Однако если паскаль среагирует на этот символ, то произойдёт глюк (описаный в команде write для текстового файла). Для файлов нетекстового типа проблемм с этим символом нет.
          Для типизированных и нетипизировнных файлов эта команда возвращает признак окончания файла, если текущее положение находится в конце файла. Если переместить текущее положение (командой seek) в другое место файла, то после этого команда Eof вернёт false
          Простейшим примером для команды будет программа, читающая и выводящая текстовый файл на экран, пока он не кончится.
          ExpandedWrap disabled
            var
              f: text;
              s: string;
            begin.
              assign(f,paramstr(1));
              reset(f);
              while not eof(f) do
              begin
                readln(f,s);
                writeln(s);
              end;
              close(f);
            end.


          SeekEOF

          Команда является почти полным аналогом предыдущей. Однако есть и различия.
          Вопервых она может быть использована только для текстовых файлов.
          И во вторых - при вызове этой команды не только возвращается признак окончания файла, а так-же пропускаются все символы, меньше #33 (пробелы и управляющие символы). Если в файле встречается символ #26, то эта функция возвращает true - тоесть это признак конца файла. При помощи этой команды удобно читать из файла только буквы или цифры, пропуская пробелы, знаки табуляции, символы перехода строки. В результате можно получить текст "сплошняком".
          Синтаксис аналогичен команде Eof.
          Вот пример:
          ExpandedWrap disabled
            var
              f: text;
              c: char;
            begin
              assign(f,'text.txt');
              reset(f);
              while not seekeof(f) do
              begin
                read(f,c);
                write(c);
              end;
              close(f);
            end;
          Получится текст из файла, только не будут переноситься строки, не будут проставляться пробелы, потому что как только будет выполняться команда SeekEof, так сразу все символы с кодом до #33 будут пропускаться, и в случае достижении конца файла функция вернёт значение true.

          EoLn


          Далее следует. Временное сохранение.
          Сообщение отредактировано: Some1 -
            Типизированные файлы

            Новая версия находится в энциклопедии: Операции над типизированными файлами в Турбо Паскале.

            Типичные операции с записями - это добавление,изменение,удаление и вставка записей в файлах.
            Давайте рассмотрим структуру записей файла.

            Позиция при открытии файла
            ExpandedWrap disabled
               =========
              |*| | | | |
               =========


            1) Чтобы добавить запись в файл, необходимо сначала установить позицию в конец файла командой Seek ,а затем производить запись с помощью Write.

            Позиция в конце файла
            ExpandedWrap disabled
               =========
              | | | | | |*
               =========

            Производим запись. Теперь добавилась ещё одна.
            ExpandedWrap disabled
               ===========
              | | | | | | |*
               ===========

            ExpandedWrap disabled
              type
                TStudentInfo=record
                  Name: string[30];
                  Kurs: string[20];
                  Exams: array[1..5] of byte;
                end;
               
              var F: file of TStudentInfo;
                  st: TStudentInfo;
              begin
              .......
                Seek(F,FileSize(F)); { становимся в конец файла }
                Write(F,st); { записываем запись в файл }
              .......
              end.


            2) Чтобы подправить запись, надо стать на позицию этой записи Seek(F,recordN), а затем производить запись. Аналогично предыдущему примеру, только вместо Seek(F,FileSize(F)); пишем Seek(F,recordN);.
            Не забывайте, что нумерация записей начинается с нуля.
            ExpandedWrap disabled
               =========
              | | |*| | |
               =========

            После того, как изменили запись, позиция продвинулась вперёд.
            ExpandedWrap disabled
               =========
              | | | |*| |
               =========


            3) Если надо найти запись и исправить ее, надо:
            1. установить позицию на начало, если необходимо (не надо когда только открыли файл для чтения)
            2. проверять каждую запись по нужному критерию (имя, телефон и т.д.)
            3. вернуться на одну позицию назад: Seek(F,FilePos(F)-1), т.к. после чтения записи с файла
              позиция уже продвинулась на следующую;
            4. внести изменения в записи и затем производить запись в файл.
            5. продолжать с пункта 2 пока не конец файла.
            Пример поиска и замены записи
            ExpandedWrap disabled
              type
                TStudentInfo=record
                  Name: string[30];
                  Kurs: string[20];
                  Exams: array[1..5] of byte;
                end;
               
              var
                f: file of TStudentInfo;
                st: TStudentInfo;
                who: string[30];
                found: boolean;
              begin
                   write('Кого ищем? ');
                   readln(who);
                   if who='' then exit;
               
                   assign(f,'students.dat');
                   {$I-}
                   reset(F);
                   {$I+}
                   found:=false;
                   if IOresult=0 then
                   with st do
                   while Not EOF(F) do
                   begin
                        read(F,st);
                        if name=who then  { нашли такого/ую }
                        begin
                             write('Заменить на фамилию: ');
                             readln(name);
                             found:=true;
                             seek(F,FilePos(F)-1); { вернуться на 1 позицию обратно, т.е. на позицию того, что надо заменять }
                             write(f,st);
                             break; { убрать это, если известно, что таких несколько }
                        end;
                   end;
                   close(f);
                   if Not Found
                      then writeln(Who,' не найден. Ха-ха')
                      else writeln(Who,' найден и заменен.');
                   writeln(#13#10'Жми Enter');
                   readln;
              end.


            4) Вы спросите: "А как же вставить или удалить запись в файле?"
            Ответ:
            Файлы не имеют гибкой структуры данных, т.е. нельзя "раздвинуть" записи и вставить ещё одну,
            или удалить среди записей ненужную, подвинув остальные на место удалённой. То есть можно, но так не делается.
            В этом случае вам придётся создать второй файл, а прежний удалить.

            0..i-1 , i , i+1..N
            • Удалить запись i:
              Записываем 0..i-1, пропускаем i, а потом дописываем i+1..N.
            • Вставить запись на место i:
              Записываем 0..i-1, затем нужную запись, а потом дописываем i..N.
            Сообщение отредактировано: Romtek -
            0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
            0 пользователей:


            Рейтинг@Mail.ru
            [ Script execution time: 0,1230 ]   [ 16 queries used ]   [ Generated: 13.12.24, 00:13 GMT ]