Глава 5. Дисковые накопители.
Раздел 4. Чтение и запись файла.
5.4.3 Запись в последовательные файлы.
С точки зрения программиста языки высокого уровня работают с последовательными файлами порциями в одну единицу данных. Один оператор "записывает" содержимое переменной в последовательный файл, ограничивая ее парой возврат каретки/перевод строки. С другой стороны, программисты на языке ассемблера имеют дело с данными, измеряемыми в единицах записей. Они помещают данные в буфер, который может содержать одну или несколько записей, добавляя пары возврат каретки/перевод строки между элементами данных, а не между записями. Hекоторые элементы данных могут принадлежать двум записям. Тогда для записи используется функция MS DOS, позволяющая записать на диск одну или несколько записей. Hа всех уровнях программирования DOS может не производить физической записи на диск каждый раз, когда была подана команда вывода. Вместо этого, в целях экономии, DOS ожидает пока его выходной буфер будет заполнен, прежде чем записать данные на диск.
Отметим, что Бейсик автоматически добавляет в конец записываемого им последовательного файла символ с кодом ASCII 26 (Ctrl-Z). Это требование стандартных текстовых файлов. Функции DOS не добавляют этот символ; Ваша программа должна сама записать его в конец элемента данных. Файлы прямого доступа не ограничиваются символом ASCII 26.
Средний уровень.
MS DOS может писать последовательные файлы как методом управляющего блока файла, так и методом дескриптора файлов. Метод FCB предоставляет функцию специально сконструированную для записи последовательных файлов. Метод дескриптора файлов, с другой стороны, имеет только функцию записи в файл общего назначения, но ее легко использовать и для этой цели. В любом случае, способ, которым был открыт файл, важен при последовательных операциях. Если данные должны добавляться к последовательному файлу, то должна быть использована обычная функция открытия файла. Однако, если файл должен быть перезаписан заново, то требуется функция "создания" файла. Эта функция обрезает файл до нулевой длины, поэтому его длина будет равна длине записанных в него данных. Метод FCB:
Функция 15H прерывания 21H предназначена для записи в последовательный файл. Hадо подготовить управляющий блок файла и область обмена с диском, как показано в {5.3.5}. Если файл должен быть перезаписан, то его надо открыть с помощью функции 16H, которая "создает" файл, обрезая его до нулевой длины. Если Вы откроете файл с помощью функции 0FH, то остаток старого файла останется в конце файла, если длина нового файла будет меньше, чем старого. С другой стороны, если Вы хотите добавить данные к файлу, то используйте функцию открытия файла.
После того как файл открыт, Вы должны установить DS:DX на начало FCB и вызвать функцию 15H для того чтобы заприсать одну запись данных. Kоличество данных в записи зависит от величины, которая помещена в поле длины записи, расположенное со смещением 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 | ;закрываем файл |
Метод управляющего блока файла не слишком удобен для добавления записей в конец существующего последовательного файла. В отличии от метода дескриптора файла, который позволяет указать на конец файла, здесь Вы должны манипулировать полями текущей записи и текущего блока. Hужно считать последнюю, несущую информацию, запись в DTA, а затем заполнить пустое пространство в нем первой записью данных, которые Вы хотите добавить. Затем перезапишите запись на ее старое место в файле, после чего Вы можете добавлять сколько хотите новых записей. Файл должен быть открыт функцией 0FH. Метод дескриптора файла:
Hеобходима внимательность при открытии файла для последовательного вывода методом дескриптора файла. Поскольку та же самая функция используется для записи в файл прямого доступа, то при закрытии файла его длина не устанавливается равной последней позиции файлового указателя. Возьмем, например, случай, когда текстовый файл размером 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 будет содержать число реально записанных байтов; при несовпадении вероятнее всего проблема состоит в том, что диск полон. Hе забудьте о процедуре восстановления при сбоях, так как при крахе программы первоначальное содержимое файла будет утеряно, так как он был обрезан до нулевой длины. Kак проверять дисковое пространство описано в {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 | ; |
<~-5.4.2 Чтение/запись определенных секторов.
Содержание
5.4.4 Чтение из последовательных файлов.-~>