IX.2. Ф а й л ы д а н н ы х п р я м о г о д о с т у п а Я знаю, что положил это в надежное место, но теперь не могу вспомнить,в какое именно! Из диалога на приеме у врача Как мы уже отмечали (см. главу V), файл данных п р я м о г о доступа представляет собой последовательность нумерованных групп значений, называ- емых з а п и с я м и. Запись представляет собой единицу хранимой в файле информации. Размер записи зависит от решаемой Вами задачи. Запись есть совокупность сведений о некотором объекте. Она образуется из п о л е й, или элементов данных, указывающих на различные атрибуты, присущие конкретному объему. Так, если "объектами" для зубного врача являются пациенты,то каждая за- пись должна хранить информацию о каком-либо пациенте, а ее полями могут быть имя пациента, его возраст, номер телефона и число поставленных пломб. На рисунке показано зрительное представление организации файла прямого доступа. Записи с данными хранятся на дискете. При каждом запросе програм- мы на чтение или на запись должен быть указан н о м е р з а п и с и, по которому во внутреннем справочнике находится ее фактическое положение на дискете, что позволяет немедленно произвести чтение или запись. Таким об- разом осуществляется прямой доступ к записям, требующий примерно одного и того же небольшого (зависящего от скорости вращения дискеты) времени для всех записей.┌──────────────────────────────┐ ┌──────────────────┐ │ ◀───▶ Запись с данными │ Запись │ Внутренний справочник файла, │ └──────────────────┘ ────▶────◀──┤ связывающий номер записи с │ ··· Чтение│ее физическим местонахождением│ ┌──────────────────┐ │ ◀───▶ Запись с данными │ └──────────────────────────────┘ └──────────────────┘ Нумерация записей выполняется последовательно, начиная с нуля. В запись объединяются логически связанные между собой данные, и номер записи обыч- но связывается с этими данными естественным образом. Например, если записи содержат информацию о студентах некоторой группы, то в качестве номеров записей можно использовать номера студентов в спис- ке группы. Во время выполнения операции в ы в о д а для размещения записи будет выбрано место, определяемое ее номером. При выполнении операции ввода по номеру отыскивается запись и передается в память. ┌─────────────────────────────────────────────────────────────────────┐ │ П р я м о й доступ к файлу целесообразен в том случае, когда тре- │ │ буется часто менять содержимое записей и просматривать их в произ- │ │ вольном порядке. │ └─────────────────────────────────────────────────────────────────────┘ Так, бронированные места в зале театра,меняющиеся от спектакля к спектак- лю уместно хранить в файле прямого доступа. Вы, как программист, должны предусмотреть размещение каждого символа в файле прямого доступа. В этом есть смысл: можно определить структуру фай- ла в соответствии с Вашими потребностями.Вы сами регулируете поток данных в файле, и поэтому важно тщательно продумать его структуру. Разработка структуры файла не составит для Вас труда,если ясно представлять себе,что происходит в программе. Перейдем теперь к детальному описанию средств языка MSX-BASIC, предна- значенных для работы с файлами данных п р я м о г о доступа. IX.2.1. К о н т р о л ь н ы е б у ф е р ы ф а й л о в ┌───────────────┐ Оператор │ MAXFILES=A │ , └───────────────┘ где: MAX, FILES - служебные слова; А - арифметическое выражение, целая часть значения которого принад- лежит отрезку [0,15], резервирует А+1 участок оперативной памяти для последующего в р е м е н - н о г о хранения записей конкретных файлов. Например: 10 MAXFILES=5 50 MAXFILES=X+Y MAXFILES=3 Эти участки называются б у ф е р а м и или, более полно, к о н т - р о л ь н ы м и б у ф е р а м и файлов ("Field Control Buffers" - FCB) и обозначаются: FCB 0, FCB 1,... . ┌────────────────────────────────────────────────────────────────────┐ │ Каждый FCB занимает 265 байтов, из которых 9 байтов предназначены │ │ для управляющей информации, а 256 байтов - для данных. │ └────────────────────────────────────────────────────────────────────┘ Следует иметь в виду,что FCB0 и FCB1 объявлены по умолчанию и что FCB0 используется многими операторами (SAVE, LOAD, MERGE, RUN и т.п.). При выполнении оператора MAXFILES= кроме резервирования буферов произ- водится "чистка" значений переменных и закрытие всех ранее открытых фай- лов данных. При выполнении операции ч т е н и я запись из файла помещается в кон- трольный буфер файла, а при выполнении операции вывода, наоборот, содержи- мое буфера переносится в файл, в область, отведенную для записи с указан- ным номером. После выполнения операции чтения данные необходимо выбирать из буфера файла, а перед выполнением операции в ы в о д а данные должны быть поме- щены в буфер файла. ┌──────────────────────┐ Функция │ VARPTR(#n) │ , └──────────────────────┘ где: VARPTR("VARiable PoinTeR"-"указатель переменной") - служебное слово; n - арифметическое выражение, целая часть значения которого определя- ет номер FCB (от 0 до значения выражения, стоящего в правой части операто- ра MAXFILES= ), возвращает а д р е с X байта, начиная с которого расположен в памяти FCBn. Если файл не существует, то выдается сообщение об ошибке: "Illegal function call". Приведем простейший пример: 10 MAXFILES=2:X=VARPTR(#0):Y=VARPTR(#1):Z=VARPTR(#2) 20 PRINT Y-X;Z-Y run 265 265 Ok Взгляните на схему расположения информации в FCBn : Управляющая информация ────┐ Д а н н ы е ┌───┬───┬─▼─┬───┬───┬───┬───┐ │▧▧▧│▧▧▧│···│▧▧▧│███│···│███│ └───┴───┴───┴───┴───┴───┴───┘ Адреса ──▶ X X+1 X+8 X+9 X+264 ▲ │ Байт, адрес которого возвращает функция VARPTR(#n) К о н т р о л ь н ы й б у ф е р ф а й л а (FCB) ┌─────┬────────┬──────────────────────────────────────┐ │Номер│ И м я │ С о д е р ж и м о е │ │байта│ байта │ б а й т а │ ├─────┼────────┼──────────────────────────────────────┤ │ X │ FL.MOD │ Тип файла: │ │ │ │ 1 - OPEN FOR INPUT │ │ │ │ 2 - OPEN FOR OUTPUT │ │ │ │ 2 - OPEN FOR APPEND (для диска) │ │ │ │ 4 - файл прямого доступа (для диска) │ │ │ │ 4 - файл последовательного доступа, │ │ │ │ открытый и для чтения, и для записи, │ │ │ │ например, OPEN "COM:" AS#1 │ │ │ │ 8 - OPEN FOR APPEND │ │ │ │ (для устройства MEM:) │ ├─────┼────────┼──────────────────────────────────────┤ │ X+1 │ FL.FCA ◀─┐ Ссылка на область памяти, в которой│ ┌───────┤ X+2 │ FL.LCA ◀─┘ хранится с п е ц и ф и к а ц и я │ │ │ │ │ файла │ │ ├─────┼────────┼──────────────────────────────────────┤ │ │ X+3 │ FL.LSA │ ? │ │ ├─────┼────────┼──────────────────────────────────────┤ │ │ X+4 │ FL.DSK │ Номер текущего устройства: │ │ │ │ │ 0 - активное устройство по умолчанию;│ │ │ │ │ 1 - устройство A: │ │ │ │ │ 2 - устройство B: │ │ │ │ │ 3 - устройство C: │ │ │ │ │ 4 - устройство D: │ │ │ │ │ 5 - устройство E: │ │ │ │ │ 6 - устройство F: │ │ │ │ │ 7 - устройство G: │ │ │ │ │ 8 - устройство H: │ │ │ │ │ &hFC - устройство GRP: │ │ │ │ │ &hFD - устройство CRT: │ │ │ │ │ &hFE - устройство LPT: │ │ │ │ │ &hFF - устройство CAS: │ │ │ │ │ Нестандартные устройства: │ │ │ │ │ &hD4 - устройство COM: │ │ │ │ │ (локальная сеть MSX-1) │ │ │ │ │ &hC0 - устройство MEM: │ │ ├─────┼────────┼──────────────────────────────────────┤ │ │ X+5 │ FL.SLB │ ? │ │ │ X+6 │ FL.BPS │ ? │ │ │ X+7 │ FL.FLG │ ? │ │ │ X+8 │ FL.OPS │ ? │ │ ├─────┼────────┼──────────────────────────────────────┤ │ │ X+9 │ FL.BUF │ Содержимое записи файла (256 байтов) │ │ └─────┴────────┴──────────────────────────────────────┘ │ С п е ц и ф и к а ц и я ф а й л а │ ┌─────┬────────────────────────────────────────────────┬──────────┐ │ │Номер│ С о д е р ж и м о е │Количество│ │ │байта│ б а й т о в │ байтов │ │ ├─────┼────────────────────────────────────────────────┼──────────┤ └──▶│ +00 │ Н о м е р дисковода (0 - по умолчанию, 1 - A:)│ 1 │ │ +01 │ │ │ │ ··· │ И м я файла │ 8 │ │ +08 │ │ │ │ +09 │ Р а с ш и р е н и е имени файла │ 3 │ ├─────┼────────────────────────────────────────────────┼──────────┤ │ +12 │ Номер текущего блока │ 2 │ │ +14 │ Текущий размер записи (по умолчанию равен 128) │ 2 │ │ +16 │ Р а з м е р файла в байтах │ 4 │ │ +20 │ Д а т а изменения файла │ 2 │ │ +22 │ В р е м я изменения файла │ 2 │ ├─────┼────────────────────────────────────────────────┼──────────┤ │ +24 │ Идентификатор устройства │ 1 │ │ +25 │ Расположение каталога │ 1 │ │ +26 │ Первый кластер файла │ 2 │ │ +28 │ Последний кластер файла │ 2 │ │ +30 │ Последний доступный кластер (относительно нача-│ 2 │ │ │ ла файла) │ │ ├─────┼────────────────────────────────────────────────┼──────────┤ │ +32 │ Номер текущей записи файла последовательного │ 1 │ │ │ доступа │ │ ├─────┼────────────────────────────────────────────────┼──────────┤ │ +33 │ Номер случайной записи для файла прямого доступа │ │ +34 │ (байты 33,34,35 - если размер записи больше 63;│ 4 │ │ +35 │ байты 33,34,35,36 - если размер записи меньше │ │ │ +36 │ 64) │ │ └─────┴────────────────────────────────────────────────┴──────────┘ IX.2.2. О п е р а т о р ы OPEN и CLOSE Для работы с файлами данных,уже существующими на дискетах или вновь ор- ганизуемыми, необходимо произвести их о т к р ы т и е . Делается это оператором ┌──────────────────────────────┐ │ OPEN B AS[#]n [LEN=m] │ , └──────────────────────────────┘ где: OPEN("открыть"), LEN("LENgth"-"длина") - служебные слова; B - строковое выражение, значение которого определяет имя файла; n,m - арифметические выражения, целые части значений которых опреде- ляют соответственно н о м е р контрольного буфера файла и д л и н у его записи в байтах; целая часть значения n должна принадлежать отрезку [0,6], так как дис- ковод может одновременно работать только с с е м ь ю различными файлами; целая часть значения m должна принадлежать отрезку [1,256] (по умолча- нию значение m равно 256). Все записи файла с п р я м ы м доступом имеют одинаковую длину, кото- рая (в байтах) задается параметром m . Это значение определяет способ раз- биения на записи дискового пространства, отведенного для файла. Например, если длина записи равна 50,то 6-я запись будет располагаться со смещением 300 (50╳6) от начала файла; # - необязательный символ, никак не влияющий на выполнение оператора OPEN . При выполнении оператора OPEN файлу с именем В назначается для работы контрольный буфер с номером n. Кроме того: α) с дискеты в буфер заносится управляющая информация о файле (если файл с именем B уже существует) или β) эта информация создается на дискете (если файл с именем В формирует- ся заново). ┌──────────────────────────────────────────────────────────────┐ │ Вся эта процедура и называется о т к р ы т и е м файла. │ └──────────────────────────────────────────────────────────────┘ Отметим, что один файл В не может быть открыт сразу по нескольким FCB! Например: 10 OPEN "А" AS#1 90 OPEN "klukva" AS#1 LEN=50 MAXFILES=2:OPEN"Старт"AS#2 MAXFILES=5:OPEN"Финиш"AS#3 LEN=100 ┌──────────────────────────────────────────────────────────────┐ │ Оператор OPEN открывает файл с п р я м ы м доступом, если │ │ спецификации режимов FOR INPUT, OUTPUT или APPEND опущены. │ └──────────────────────────────────────────────────────────────┘ Если программа закончит запись в файл, а в буфере останутся некоторые, не записанные на дискету данные, программа должна тем или иным способом все же завершить перепись содержимого буфера. Сделать это можно с помощью оператора з а к р ы т и я файла, к изучению которого мы и приступаем. ┌───────────────────────────────────┐ Оператор │ CLOSE [[#]N1,[#]N2,...,[#]Nk] │ , └───────────────────────────────────┘ где: CLOSE ("to close"-"закрывать") - служебное слово; N1,N2,...,Nk - арифметические выражения, целые части значений кото- рых должны принадлежать отрезку [0,15], организует з а к р ы т и е открытых с помощью оператора OPEN файлов,име- ющих номера контрольных буферов файлов,равных значениям выражений: N1, N2,..., Nk . Оператор CLOSE без параметров закрывает в с е открытые на данный мо- мент файлы данных. Однако большинство программистов предпочитают иметь гарантированный точный результат работы оператора CLOSE и поэтому указывают в нем номера файлов в явном виде. З а к р ы т ы й файл становится недоступным для формирования новых или обновления оператором PUT уже имеющихся записей. Приведем примеры синтаксиса оператора CLOSE : CLOSE CLOSE #0 CLOSE 1.5,7 ┌───────────────────────────────────────────────────────────────────┐ │ Заметим, что закрытие всех ранее открытых файлов происходит также │ │ при выполнении операторов END, CLEAR, LOAD, MAXFILES=, NEW и │ │ любом и з м е н е н и и текста программы ! │ └───────────────────────────────────────────────────────────────────┘ Пока файл не закрыт, существует опасность, что прекращение работы про- граммы - из-за ошибки пользователя, отключения электроэнергии или ошибки в программе - приведет к утрате данных или даже к повреждению дискеты. Опыт подсказывает, что желательно закрывать файл, как только исчезнет не- обходимость в том, чтобы он был открыт. IX.2.3. О п е р а т о р FIELD Как мы уже отмечали,под каждый контрольный буфер файла (FCB) отводится 265 байтов, 256 из которых непосредственно предназначены для временного хранения з а п и с и файла. Перед началом непосредственной работы с контрольным буфером файла необ- ходимо произвести его разбиение на отдельные п о л я для формирования в них конкретных значений. Делается это при помощи оператора ┌─────────────────────────────────────────────────────┐ │ FIELD [#]n, M1 AS γ1, M2 AS γ2, ..., Mk AS γk │ , └─────────────────────────────────────────────────────┘ где: FIELD ("field"-"поле") - служебное слово; n - арифметическое выражение, целая часть значения которого опреде- ляет номер контрольного буфера файла; M1,M2,...,Mk - целые числа. Учтите, что суммарная длина всех объявляемых в операторе FIELD полей не должна превосходить длины записи,установленной оператором OPEN. Нарушение этого правила приводит к ошибке; γ1,γ2,...,γk - строковые переменные (простые или с индексами), назы- ваемые б у ф е р н ы м и переменными; # - необязательный символ,никак не влияющий на выполнение оператора. При выполнении команды FIELD производится задание формата записи. Это выражается в выделении каждому значению буферной переменной γs п о л я из Ms байтов (s=1,2,...k). Ни одну из буферных переменных оператора FIELD нельзя использовать ни в каких модификациях оператора INPUT и ни одной из них нельзя присваивать значение оператором LET. Нарушение любого из этих правил приводит к утра- те соответствия, установленного оператором FIELD, вследствие чего програм- ма не может больше использовать такую переменную для занесения нужных зна- чений в записи файла или для их извлечения. Разбиение FCB на поля оператором FIELD покажем на рисунке: Управляющая информация─┐ M1 M2 ··· Mk (9 байтов) ┌────▼───┬─────────┬─────┬───┬───────────┐ │████████│ │ │···│ │ └────────┴────▲────┴──▲──┴───┴─────▲─────┘ │ │ │ Значения ───▶ γ1 γ2 ··· γk Например, оператор FIELD #1,200 AS X$,12 AS Y$ отводит 200 байтов для переменной X$ и 12 байтов - для переменной Y$. ┌─────────────────────────────────────────────────────────────────┐ │ Заметим, что этот оператор будет выполнен только в том случае, │ │ если файл был предварительно открыт. │ └─────────────────────────────────────────────────────────────────┘ П р и м е р 1. В качестве примера рассмотрим файл с прямым доступом,со- ───────────── держащий информацию о личной библиотеке. Каждая запись содержит следующую информацию: 1) номер книги (2 байта); 2) фамилию автора книги (15 байтов); 3) название книги (30 байтов); 4) признак наличия книги (1 байт); 5) информацию о читателе, взявшем книгу (22 байта); 6) дату выдачи книги (10 байтов). Для открытия этого файла и описания структуры записи можно использо- вать следующие операторы: 10 OPEN "Библтека" AS#1 LEN=80 'В имени - не более 8 символов! 20 FIELD #1,2 AS NUMBER$,15 AS AUTHOR$,30 AS BOOK$,1 AS CODE$, 22 AS R ADER$,10 AS DATE$ Отметим, что разрешается использовать несколько команд FIELD с разными форматами для одного и того же буфера n. Всегда действует то разбиение FCBn на поля, которое предложено последней выполненной командой FIELD. П р и м е р 2. ───────────── 10 MAXFILES=3:OPEN"Student" AS#2 LEN=40 20 FIELD #2,28 AS M1$,12 AS M2$ 30 FIELD #2,10 AS M1$,5 AS M2$,24 AS K$ ┌─────────────────────────────────────────────────────────────────┐ │ Оператор FIELD должен выполняться перед обращениями к файлу с │ │ помощью операторов PUT и GET (см. раздел IX.2.6). │ └─────────────────────────────────────────────────────────────────┘ Прежде чем обратиться к файлу, Вы должны продумать, какие данные будут составлять записи, каковы их тип и длина (размер). В результате Вы получи- те документ, часто называемый л о г и ч е с к о й с т р у к т у р о й ф а й л а. С ним Вам предстоит работать в течение всего времени "жизни" файла. Поскольку в этом документе указано число байтов, отведенное под каждую запись, можно заранее установить количество байтов, которое потре- буется на дискете для конкретного файла, а так как известен и тип применя- емых данных, то становится очевидным, какие придется вводить переменные. Важно отметить,что при известной логической структуре файла его в дальней- шем могут использовать другие программы. На рисунке изображена структура записи в файле и дан расчет размера файла для 24 записей о пловцах. Каждая запись содержит поля для имени пло- вца, клуба,к которому он принадлежит, дисциплины, в которой он выступает, а также поля для его возраста и лучшего показанного результата. ┌───────────────────────────────────────────────────────────────────────┐ │ И м я ф а й л а: SWIMMER . Т и п ф а й л а: прямого доступа . │ │ С о д е р ж и м о е: 24 записи о пловцах . │ ├──────────────┬────────────────────────┬──────────────────┬────────────┤ │Имя переменной│ О п и с а н и е │Д л и н а п о л я│ Т и п │ │ │ п о л я │ (б а й т) │ д а н н ы х│ ├──────────────┼────────────────────────┼──────────────────┼────────────┤ │ N$ │ Имя пловца │ 30 │30 символов │ │ T$ │ Принадлежность к клубу │ 20 │20 символов │ │ E$ │ Дисциплина │ 8 │ 8 символов │ │ A$ │ Возраст │ 2 │Целое число │ │ T │ Лучший результат │ 4 │ Вещ. число │ └──────────────┴────────────────────────┴──────────────────┴────────────┘ Итого: 64 Длина и тип каждого поля указаны вместе с переменными, которые в Вашей программе будут хранить данные, извлеченные из записей. Совершенно необя- зательно использовать одни и те же переменные во всех программах, обращаю- щихся к этому файлу, однако такое единообразие позволит легче проследить ход обработки данных в любой программе. Еще раз подчеркнем, что длина записи в файле с прямым доступом п о - с т о я н н а . Она определяется при его создании, и каждому полю выделя- ется внутри записи строго определенное место.Возможные значения полей дол- жны умещаться в этом заранее отведенном объеме памяти. Если это не проду- мано заблаговременно, то при выполнении программы чрезмерно "длинные" зна- чения подвергаются усечению, а "короткие" дополняются пустующими ячейками памяти. В целях минимизации нежелательного усечения размер каждого поля обычно выбирается достаточно большим, чтобы вместить самую большую возмож- ную величину. IX.2.4. О п е р а т о р ы LSET и RSET Напомним Вам, что команда FIELD определяет формат записи файла и набор буферных переменных. Далее буферные переменные не могут использоваться в операторах LET, INPUT, LINE INPUT. При формировании в памяти конкретной записи значения буферным перемен- ным из FIELD присваиваются операторами: ┌──────────────────┐ │ LSET γ=β │ │ RSET γ=β │ , └──────────────────┘ где: LSET ("Left SET" - "поместить слева"), RSET ("Right SET" - "поместить справа") - служебные слова; γ - имя буферной переменной; β - строковое выражение (β≠γ). Пусть буферной переменной γ отведено в буфере поле размером m байтов. Выполнение оператора LSET начинается с вычисления значения выражения β. Далее, если это необходимо, его длина приводится к величине m за счет отбрасывания лишних п р а в ы х символов или добавления справа недостаю- щего количества пробелов. Полученное значение присваивается буферной пере- менной γ и размещается в соответствующем поле буфера файла. Оператор RSET выполняется почти так же, как и оператор LSET.Разница со- стоит в том, что при выравнивании значения β до длины m возможные недоста- ющие пробелы добавляются к нему не справа, а с л е в а. Неиспользованные байты заполняются пробелами. Таким образом, операторы LSET и RSET гаранти- руют, что длина значения переменной будет приведена в соответствие с дли- ной, указанной в операторе FIELD. П р и м е р 1. 10 OPEN "EF" AS#1 LEN=9 ───────────── 20 FIELD #1,5 AS M$,14 AS L$ 30 INPUT R$:LSET M$=R$ 40 INPUT R$:RSET L$=R$ 50 PRINT M$:PRINT L$ run ? КРАТЕР M$ ? НЕБОСВОД ┌───┬───┬───┬───┬───┬ КРАТЕ │ K │ Р │ А │ Т │ Е │ НЕБОСВОД └───┴───┴───┴───┴───┴ Ok 5 байтов П р и м е р 2. Приведем фрагмент программы, в котором формируется за- ───────────── пись файла, содержащего информацию о личной библиотеке. В буфер заносится информация о романе Л.Н.Толстого "Война и мир". Структу- ра записи была рассмотрена ранее. 10 OPEN "Библтека" AS#1 LEN=80 20 FIELD #1,2 AS NUMBER$,15 AS AUTHOR$,30 AS BOOK$,1 AS CODE$, 22 AS R ADER$,10 AS DATE$ 30 LSET NUMBER$=10 'Шифр книги 40 LSET AUTHOR$="Л.Н.Толстой" 50 LSET BOOK$="Война и мир" 60 LSET CODE$="X" 'X - книга хранится 70 LSET READER$=" " 'Читатель, разумеется, отсутствует 80 LSET DATE$="10.5.1988" Команды LSET и RSET можно использовать не только при работе с файлами. П р и м е р 3. 10 L$=SPACE$(20):Z$="Волк" ───────────── 20 RSET L$=Z$:PRINT L$