5.4.3 Запись в последовательные файлы.

Error message

Notice: Undefined index: add term path in hansel_get_breadcrumbs() (line 524 of /data/sites/msdosworld.ru/www/sites/all/modules/hansel/hansel.module).

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


уровнях  программирования  DOS  может  не  производить физической
записи  на  диск  каждый раз, когда была подана  команда  вывода.
Вместо этого, в  целях  экономии,  DOS  ожидает пока его выходной
буфер будет заполнен, прежде чем записать данные на диск.
   Отметим, что Бейсик автоматически добавляет в конец записывае-
мого им последовательного файла символ с кодом ASCII 26 (Ctrl-Z).
Это требование стандартных текстовых файлов.  Функции DOS не  до-
бавляют этот символ;  Ваша  программа  должна сама записать его в
конец  элемента данных.  Файлы прямого доступа не  ограничиваются
символом ASCII 26.

   Высокий уровень.

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

   100 OPEN "MYFILE" FOR OUTPUT AS #1

или

   100 OPEN "O", #1, "MYFILE"

Во  второй форме буква "O" обозначает вывод (output).  Символ  #1
обозначает кодовый  номер,  по  которому  Вы  будете впоследствии
обращаться  к файлу в операторах доступа, таких как WRITE #1  или
INPUT #1. В обоих  случаях  открывается  файл с именем MYFILE для
приема данных в последовательном режиме. Если файл с таким именем
не найден на диске, то оператор OPEN  создаст его.  Если же такой
файл существует, то он будет перезаписан, т.е. после его закрытия
он будет содержать только новые  записанные  в него данные. Чтобы
добавить данные в конец существующего последовательного файла, не
изменяя его предыдущего содержимого, нужно открыть его, используя
первую форму оператора OPEN  в  виде  OPEN "MYFILE" FOR APPEND AS
#1. Более подробно об этом см. [5.3.3].
   Данные  записываются в файл с помощью операторов PRINT# и WRI-
TE#. Они имеют одинаковую форму:

   100 PRINT #1, S$

или

   100 WRITE #1, X

#1 относится к идентификационному  номеру файла (дескриптору фай-
ла), присваиваемому ему оператором OPEN.  В первом примере в файл
записывается значение строковой переменной, а во втором численное
значение, но можно любым из них записывать и то и другое. Числен-
ные значения записываются  в  последовательные  файлы в строковом
виде,  хотя они и берутся не из строковых переменных.   Например,
232 является 2-хбайтным целым в строковой  форме, однако если X =
232, то оператор PRINT #1, X помещает в файл три байта, используя
коды ASCII для цифр 2, 3 и 2.


   Операторы PRINT# и WRITE#  отличаются  способом отделения эле-
ментов данных в файле. Какой из них более подходящий определяется
характеристиками данных. Основное различие между двумя оператора-
ми состоит в том, что WRITE# вставляет дополнительные ограничите-
ли между элементами  данных.  Рассмотрим  случай,  когда оператор
выводит  несколько переменных в виде 100 PRINT #1, A$, Z, B$  или
100 WRITE #1, A$, Z, B$. В этом случае пара возврат каретки/пере-
вод  строки  будет помещена в файл только за  последней  из  трех
переменных (отметим,  что  строковые  и числовые переменные могут
быть  перемешаны).   Как же можно впоследствии выделить  эти  три
переменные? Если был  использован  оператор PRINT#, то никак. Все
три переменные будут объединены в непрерывную строку. Если же был
использован оператор WRITE#, то  каждый элемент данных будет зак-
лючен  в кавычки, а между ними будут стоять запятые.  Затем,  при
чтении этих элементов из файла,  Бейсик  будет автоматически уда-
лять кавычки и запятые, которые были добавлены оператором WRITE#.
   Имеется еще ряд менее важных вопросов.  Один из них состоит  в
том, что вся проблема  с  ограничителями  может  быть снята, если
использовать  для  вывода каждой  переменной  отдельный  оператор
PRINT# или WRITE#. В этом  случае  PRINT# будет отделять все эле-
менты  парами возврат каретки/перевод строки, а WRITE# будет  де-
лать то же самое, но по-прежнему каждый  элемент будет заключен в
кавычки  (что  напрасно расходует файловое пространство).   Более
того, для вывода строк, которые  сами  содержат кавычки, оператор
WRITE# использовать нельзя, поскольку первая же внутренняя кавыч-
ка будет при чтении ошибочно  воспринята  как признак конца пере-
менной. И, наконец, отметим, что когда в одном операторе выводит-
ся несколько переменных, то оба  оператора  форматируют  данные в
точности так же, как они форматировались бы при выводе на  терми-
нал. Таким образом PRINT #1, A$, B$ отделяет B$ от A$, в то время
как  PRINT #1, A$; B$ - нет; файл будет добавляться нужным числом
пробелов.  Оператор PRINT# может быть испоьзован в форме PRINT #1
USING..., где могут быть использованы все обычные экранные форма-
ты PRINT USING для форматирования вывода в файл.
   Вообще говоря, более  экономично использовать оператор PRINT#,
записывая каждый раз по одной переменной. Этот метод избавляет от
излишних ограничителей  и  позволяет  затем безошибочно считывать
строки любого вида. Более сложные схемы ограничителей, используе-
мые при записи нескольких  переменных одним оператором PRINT# или
WRITE#  могут привести к проблемам, особенно если одна переменная
будет считана как две, что  приведет  к  потере текущей позиции в
файле.
   После  того как все данные будут записаны в файл, просто  зак-
ройте его, чтобы обезопасить содержащиеся  в нем данные. Напишите
CLOSE, чтобы закрыть все открытые файлы, CLOSE #1 - чтобы закрыть
файл #1 и CLOSE #1, #3 -  чтобы  закрыть  файлы  #1  и #3. Хотя в
некоторых случаях Бейсик прощает незакрытые файлы, но это не  тот
случай. Операторы WRITE# и  PRINT#  выводят данные в файловый бу-
фер, который записывается на диск только тогда, когда они  запол-
нены информацией. Последние введенные данные записываются на диск
оператором  CLOSE.  Отсутствие этого оператора может  привести  к
потере данных. Вот пример:

100 OPEN "A:NEWSEQ" FOR OUTPUT AS #1  'открываем файл


110 A$ = "aaaaa"                      'готовим три строки
120 B$ = "bbbbb"                      '
130 C$ = "ccccc"                      '
140 WRITE #1, A$, B$, C$              'запись строк
150 CLOSE                             'очистка буфера

   Средний уровень.

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

Метод FCB:
   Функция 15H прерывания 21H предназначена для записи в последо-
вательный файл. Надо подготовить управляющий блок файла и область
обмена  с диском, как показано в [5.3.5].  Если файл должен  быть
перезаписан, то его надо  открыть  с помощью функции 16H, которая
"создает"  файл, обрезая его до нулевой длины.  Если Вы  откроете
файл с помощью функции 0FH, то остаток  старого файла останется в
конце файла, если длина нового файла будет меньше, чем старого. С
другой стороны, если Вы хотите  добавить  данные  к файлу, то ис-
пользуйте функцию открытия файла.
   После  того  как файл открыт, Вы должны  установить  DS:DX  на
начало FCB и вызвать  функцию  15H  для того чтобы заприсать одну
запись  данных.  Количество данных в записи зависит от  величины,
которая помещена в поле длины записи,  расположенное со смещением
14  в  обычном FCB, по умолчанию это значение равно  128  байтам.
Если размер записи меньше, чем размер  сектора диска 512 байт, то
данные будут буферизоваться, до тех пор пока не накопится  доста-
точно данных, чтобы произвести  реальную  запись на диск; поэтому
записи  в последовательный файл могут успешно  записываться  даже
если накопитель не включен. При  закрытии файла все данные остав-
шиеся в буфере сбрасываются на диск. При возврате из функции 15H,
AL равен 0, если операция успешна, 1 - если диск полон и 2 - если
сегмент области обмена данных слишком мал.
   В  следующем примере на диск записываются 5 записей длиной 256
байтов.  Записи могут быть набором  текстовых данных.  Эти данные
расположены в области памяти, помеченной меткой WORKAREA.  Указа-
тель на DTA первоначально устанавливается на начало этой области,
а  после записи каждой записи установка DTA меняется таким  обра-
зом, чтобы он указывал на 256  байтов  выше.  Отметим, что обычно
для  такой  рабочей области отводится специальная область  памяти
[1.3.1], но в данном  примере  для  простоты  используется  буфер
расположенный в сегменте данных.


;---в сегменте данных
WORKAREA     DB  2000 DUP (?)   ;буфер данных
FCB          DB  1,'FILENAMEEXT',25 DUP (0)

;---DTA должен указывать на рабочую область
   LEA  DX,WORKAREA   ;DS:DX указывают на DTA
   MOV  DI,DX         ;сохраняем копию
   MOV  AH,1AH        ;функция установки DTA
   INT  21H           ;устанавливаем DTA
;---открываем файл
   MOV  AH,16H        ;номер функции
   LEA  DX,FCB        ;DS:DX указывают на FCB
   INT  21H           ;открываем файл
;---устанавливаем размер записи
   LEA  BX,FCB        ;BX указывает на FCB
   MOV  AX,256        ;размер записи 256 байтов
   MOV  [BX]+14,AX    ;записываем в поле размера записи
;---посылаем данные в файл
   MOV  CX,5          ;число записей
NEXT_REC:  MOV  AH,15H   ;функция записи
   LEA  DX,FCB        ;указываем на FCB
   INT  21H           ;записываем данные
   CMP  AL,2          ;проверка на ошибки
   JE   CONTINUE      ;и их обработка
   CMP  AL,1          ;
   JE   DISK_FULL     ;
;---перенос выполнен, переустанавливаем DTA
   ADD  DI,256        ;сдвигаемся на 1 запись
   MOV  DX,DI         ;DS:DX указывают на новый DTA
   MOV  AH,1AH        ;функция установки DTA
   INT  21H           ;установка новой позиции
   LOOP NEXT_REC:     ;идем на следующую запись

;---позднее, закрываем файл
   LEA  DX,FCB        ;DS:DX указывают на FCB
   MOV  AH,10H        ;функция закрытия файла
   INT  21H           ;закрываем файл

   Метод управляющего блока файла не слишком удобен для  добавле-
ния записей в  конец  существующего  последовательного  файла.  В
отличии от метода дескриптора файла, который позволяет указать на
конец файла, здесь Вы должны манипулировать полями текущей записи
и текущего блока.  Нужно  считать  последнюю, несущую информацию,
запись в DTA, а затем заполнить пустое пространство в нем  первой
записью данных, которые Вы  хотите  добавить.  Затем перезапишите
запись на ее старое место в файле, после чего Вы можете добавлять
сколько хотите новых  записей.  Файл  должен быть открыт функцией
0FH.

Метод дескриптора файла:
   Необходима  внимательность  при открытии файла для  последова-
тельного вывода методом дескриптора  файла. Поскольку та же самая


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

Если файл был открыт  простой  командой  открытия  файла (функция
3DH),  то  после того, как новая, более  короткая,  версия  файла
будет записана на диск и файл  будет  закрыт, его длина останется
равной 2000 байтам, из которых новый текст будет занимать  первую
тысячу байтов. По этой  причине,  при  открытии последовательного
файла для перезаписи надо использовать функцию 3CH прерывания 21H
[5.3.2].  Эта функция обычно создает новый файл, но если файл уже
существует, то он  обрезается  до  нулевой  длины. Для добавления
данных  в последовательный файл надо использовать обычную функцию
открытия файла, 3DH прерывания 21H [5.3.3].
   Рассмотрим сначала случай полной перезаписи файла. После того,
как файл открыт функцией 3CH, файловый указатель  устанавливается
равным нулю, поэтому нет нужды устанавливать его. Поместите номер
файла  в BX, а число записываемых байтов в CX.  Затем  установите
DS:DX на первый байт  выводимых  данных  и  выполните функцию 40H
прерывания 21H.  При возврате, если флаг переноса установлен,  то
была ошибка и AX содержит 5, если была ошибка дискового накопите-
ля и 6 - если неверный номер файла.  В противном случае, AX будет
содержать число реально  записанных  байтов; при несовпадении ве-
роятнее всего проблема состоит в том, что диск полон. Не забудьте
о процедуре восстановления при сбоях, так как при крахе программы
первоначальное  содержимое  файла будет утеряно, так как  он  был
обрезан до нулевой длины.  Как  проверять  дисковое  пространство
описано в [5.1.2]. Вот пример:

;---в сегменте данных
PATH         DB    'B:FILENAME.EXT',0   ;путь к файлу
DATA_BUFFER  DB    2000 DUP (?)

;---открываем файл с помощью функции "создания"
   LEA  DX,PATH          ;DS:DX указывают на путь к файлу
   MOV  CX,0             ;атрибуты файлы (здесь обычные)
   MOV  AH,3CH           ;номер функции
   INT  21H              ;открываем файл
   JC   OPEN_ERROR       ;проверка на ошибку
   MOV  HANDLE,AX        ;запоминаем номер файла
;---записываем в файл 1000 байтов
   MOV  AH,40H           ;номер функции
   MOV  BX,HANDLE        ;номер файла в BX
   MOV  CX,1000          ;число байт, которые надо записать
   LEA  DX,DATA_BUFFER   ;DS:DX указывают на буфер данных
   INT  21H              ;записываем данные
   JC   OUTPUT_ERROR     ;проверка на ошибки
   CMP  CX,2000          ;и их обработка
   JNE  FULL_DISK        ;


   Для  добавления записей в последовательный файл  надо  открыть
файл с помощью функции 3DH  прерывания  21H, помещая 1 в AL, если
программа будет только писать данные и 2, если программа будет  и
читать и писать. Длина файла  остается  неизменной, хотя он будет
увеличиваться по мере добавления данных.  Файловый указатель дол-
жен быть установлен на  конец  файла,  иначе  существующие данные
будут перезаписаны.  Это выполняется функцией 42H прерывания 21H.
Поместите номер подфункции 2  в  AL,  для  установки указателя на
конец  файла,  а номер файла поместите в BX.  CX:DX указывают  на
смещение  относительно  конца  файла,  начиная  с  которого будет
производиться запись, поэтому обнулите эти регистры. Затем выпол-
ните функцию установки указателя. При возврате установленный флаг
переноса  индицирует  ошибку, при этом в AX будет 1,  если  номер
подфункции в AL был неверен, и 6  - если неверно был указан номер
файла.   После  того как файловый указатель  установлен  операция
записи выполняется в точности как в предыдущем случае:

;---в сегменте данных
PATH          DB  'B:FILENAME.EXT',0   ;путь к файлу
DATA_BUFFER   DB  1000 DUP(?)

;---открываем файл
   LEA  DX,PATH       ;DS:DX указывают на путь
   MOV  AL,1          ;код открытия только для записи
   MOV  AH,3DH        ;номер функции
   INT  21H           ;открываем файл
   JC   OPEN_ERROR    ;уход по ошибке
   MOV  HANDLE,AX     ;сохраняем номер файла
;---установка файлового указателя на конец файла
   MOV  BX,AX         ;номер файла в BX
   MOV  CX,0          ;CX:DX дают смещение относительно конца
   MOV  DX,0          ;
   MOV  AL,2          ;код для конца файла
   MOV  AH,42H        ;функция установки указателя
   INT  21H           ;устанавливаем указатель
   JC   POINTER_ERROR ;проверка на ошибку
;---добавляем к файлу 300 байтов
   MOV  AH,40H        ;номер функции
   MOV  BX,HANDLE     ;номер файла в BX
   MOV  CX,300        ;число записываемых байтов
   LEA  DX,DATA_BUFFER   ;DS:DX указывают на буфер данных
   INT  21H           ;добавляем данные
   JC   OUTPUT_ERROR  ;проверка на ошибки
   CMP  CX,300        ;и их обработка
   JNE  FULL_DISK     ;

 

Вы находитесь в разделе: 

Добавить коментарий