Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система MS-DOS

© Александр Фролов, Григорий Фролов
Том 1, книга 3, М.: Диалог-МИФИ, 1992.

[Назад] [Содеожание] [Дальше]

3.5. Чтение/запись файлов

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

На входе: AH = 40h
BX = файловый индекс открытого файла
CX = количество записываемых байтов
DS:DX = Адрес буфера, содержащего записываемые данные
На выходе: AX = Код ошибки, если был установлен в 1 флаг переноса CF;
Количество действительно записанных байтов, если флаг переноса CF сброшен в 0.

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

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

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

Следует учитывать, что количество действительно записанных байтов может не совпадать с заданным в регистре CX при вызове функции 40h. Такая ситуация возможна, например, при записи в файл, открытый в текстовом режиме, байта Ctrl-Z (1Ah). Этот байт означает конец текстового файла. Другая возможная причина - отсутствие свободного места на диске.

Если функция вызывается с содержимым регистра CX, равным 0, файл будет обрезан или расширен до текущего положения файлового указателя.

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

Функция 40h может выполнять запись не только в файл, но и в устройство посимвольной обработки, предварительно открытое функцией 3Dh. Об этом мы говорили в разделах книги, посвященных драйверам.

Для чтения данных из файла (или устройства посимвольной обработки) предназначена функция 3Fh прерывания INT 21h:

На входе: AH = 3Fh
BX = файловый индекс открытого файла
CX = количество читаемых байтов
DS:DX = Адрес буфера для данных
На выходе: AX = Код ошибки, если был установлен в 1 флаг переноса CF;
Количество действительно прочитанных байтов, если флаг переноса CF сброшен в 0.

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

Если ваша программа составлена на языке программирования С, для записи и чтения данных она может воспользоваться функциями write() и read():

int write(int handle, void *buffer, unsigned count);

int read(int handle, void *buffer, unsigned count);

Эти функции работают аналогично функциям 40h и 3Fh прерывания INT 21h. Параметр handle определяет файл, для которого необходимо выполнить операцию записи или чтения. Параметр buffer - указатель на буфер, который содержит данные для записи или в который необходимо поместить прочитанные данные. Количество записываемых/читаемых байтов определяется третьим параметром - count.

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

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

#include <io.h>
#include <conio.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <malloc.h>
#include <errno.h>

void main(int, char *[]);
void main(int argc, char *argv[]) {

         int source, taget, i;
         char *buffer;
         unsigned count;

         if(argc == 3) {

// Открываем исходный копируемый файл

                 if((source = open(argv[1], O_BINARY | O_RDONLY)) == - 1) {

                          printf("\nОшибка при открытии исходного файла: %d",
                          errno);
                          exit(-1);

                 }

// Открываем выходной файл. При необходимости создаем
// новый. Если файл уже существует, выводим на экран
// запрос на перезапись содержимого существующего файла

                taget = open(argv[2], O_BINARY | O_WRONLY | O_CREAT | O_EXCL,
                                                 S_IREAD | S_IWRITE);
                if(errno == EEXIST) {

                        printf("\nФайл существует. Перезаписать? (Y,N)\n");

// Ожидаем ответ оператора и анализируем его

                        i = getch();
                        if((i == 'y') || (i == 'Y'))
                                taget = open(argv[2], O_BINARY | O_WRONLY | O_CREAT | O_TRUNC,
                                                                 S_IREAD | S_IWRITE);

                }

// Если выходной файл открыть невозможно, выводим
// сообщение об ошибке и завершаем работу программы

                if(taget == -1){
                          printf("\nОшибка при открытии выходного файла: %d",
                          errno);
                          exit(-1);
                }

// Будем читать и писать за один раз 10000 байтов

                count = 10000;

// Заказываем буфер для передачи данных

                if((buffer = (char *)malloc(count)) == NULL) {
                          printf("\nНедостаточно оперативной памяти");
                          exit(-1);
                }


// Копируем исходный файл

                while(!eof(source)) {

// Читаем count байтов в буфер buffer

                        if((count = read(source, buffer, count)) == -1) {
                          printf("\nОшибка при чтении: %d",
                          errno);
                          exit(-1);
                        }

// Выполняем запись count байтов из буфера в выходной файл

                        if((count = write(taget, buffer, count)) == - 1) {
                          printf("\nОшибка при записи: %d",
                          errno);
                          exit(-1);
                        }
                }

// Закрываем входной и выходной файлы

                close(source);
                close(taget);

// Освобождаем память, заказанную под буфер

                free(buffer);
         }

// Если при запуске программы не были указаны
// пути для входного или выходного файла,
// выводим сообщение об ошибке

         else
                  printf("\n"
                                "Задайте пути для исходного"
                                " и результирующего файлов!\n");
}

В приведенной программе для определения конца исходного файла использована функция eof():

int eof(int handle);

Для файла с файловым индексом handle эта функция возвращает одно из трех значений:

1 достигнут конец файла;
0 конец файла не достигнут;
-1 ошибка, например, неправильно указан handle.

Программа, которая читает файл с помощью функции 3Fh прерывания INT 21h, может определить момент достижения конца файла, анализируя код ошибки в регистре AX.

[Назад] [Содеожание] [Дальше]