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

MS-DOS для программиста

© Александр Фролов, Григорий Фролов
Том 19, М.: Диалог-МИФИ, 1995, 253 стр.

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

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

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

Запись данных в файл

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

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

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

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

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

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

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

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

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

Чтение данных из файла

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

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

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

Функции библиотеки Borland C++

Если ваша программа составлена на языке программирования С или C++, для записи и чтения данных она может воспользоваться функциями 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 Кбайт - вы можете получить признак ошибки, хотя передача данных выполнилась правильно. Большие массивы данных можно записывать по частям.

Программа FCOPY

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


Листинг 3.4. Файл fcopy\fcopy.cpp


#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>

int main(int argc, char *argv[])
{
  int source, taget, i;
  char *buffer;
  int count;

  if(argc == 3)
  {
    // Открываем исходный файл
    if((source = open (argv[1],
      O_BINARY  | O_RDONLY )) == - 1)
    {
      printf("\nОшибка: %d", errno);
      return(-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);
      return(-1);
    }

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

    // Заказываем буфер для передачи данных
    if((buffer = (char *)malloc(count)) == NULL)
    {
      printf("\nМало памяти");
      return(-1);
    }

    // Копируем исходный файл
    while(!eof(source))
    {
      // Читаем count байт в буфер buffer
      if((count = read (source, buffer, count)) == -1)
      {
        printf("\nОшибка при чтении: %d", errno);
        return(-1);
      }

      // Выполняем запись count байт
      // из буфера в выходной файл
      if((count = write (taget, buffer, count)) == - 1)
      {
        printf("\nОшибка при записи: %d", errno);
	return(-1);
      }
    }

    // Закрываем входной и выходной файлы
    close (source);
    close (taget);

    // Освобождаем память, заказанную под буфер
    free(buffer);
  }

  // Если при запуске программы не были указаны
  // пути для входного или выходного файла,
  // выводим сообщение об ошибке
  else
    printf("\nЗадайте пути для файлов!\n");
  return 0;
}

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

int eof(int handle);

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

Значение Описание
1 Достигнут конец файла
0 Конец файла не достигнут
-1 Ошибка, например, неправильно указан идентификатор файла

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

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