Чтение/запись файлов
После того, как вы открыли файл, можно выполнять над ним операции чтения/записи. Для записи данных в файл предназначена функция 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. |