Выбрать главу

На Web-сайте книги предоставляются три дополнительные программы, родственные этой: tail.с — другой пример реализации произвольного доступа к файлу, getn.c — упрощенная версия программы RecordAccess, обеспечивающая лишь чтение записей, и atouMT (включена в программы для главы 14, находящиеся на Web-сайте, однако не включена в программы, приведенные в книге), также иллюстрирующая прямой доступ к файлам.

Программа 3.1. RecordAccess 

/* Глава 3. RecordAccess. */

/* Использование: RecordAccess имя файла [количество записей]

Количество записей (nrec) можно не указывать, если файл с указанным именем уже существует. Если количество записей (nrec) задано, создается файл с указанным именем (если файл с таким именем существует, он уничтожается). При большом количестве записей (nrec) файлы рекомендуется создавать как разреженные. */

/* Программа иллюстрирует:

1. Произвольный доступ к файлам.

2. Арифметику данных типа LARGE_INTEGER и использование 64-битовых указателей файла.

3. Обновление записей на месте.

4. Запись в файл нулей во время инициализации (требует использования файловой системы NTFS).

*/

#include "EvryThng.h"

#define STRING_SIZE 256

typedef struct _RECORD { /* Структура записи в файле */

 DWORD ReferenceCount; /* 0 означает пустую запись. */

 SYSTEMTIME RecordCreationTime;

 SYSTEMTIME RecordLastReferenceTime;

 SYSTEMTIME RecordUpdateTime;

 TCHAR DataString[STRING_SIZE];

} RECORD;

typedef struct _HEADER { /* Дескриптор заголовка файла */

 DWORD NumRecords;

 DWORD NumNonEmptyRecords;

} HEADER;

int _tmain(int argc, LPTSTR argv[]) {

 HANDLE hFile;

 LARGE_INTEGER CurPtr;

 DWORD FPos, OpenOption, nXfer, RecNo;

 RECORD Record;

 TCHAR String[STRING_SIZE], Command, Extra;

 OVERLAPPED ov = {0, 0, 0, 0, NULL}, ovZero = {0, 0, 0, 0, NULL};

 HEADER Header = {0, 0};

 SYSTEMTIME CurrentTime;

 BOOLEAN HeaderChange, RecordChange;

 OpenOption = (argc == 2) ? OPEN_EXISTING : CREATE_ALWAYS;

 hFile = CreateFile(argv[1], GENERIC_READ | GENERIC_WRITE, 0, NULL, OpenOption, FILE_ATTRIBUTE_NORMAL, NULL);

 if (argc >= 3) { /* Записать заголовок и заранее установить размер нового файла */

  Header.NumRecords = atoi(argv[2]);

  WriteFile(hFile, &Header, sizeof(Header), &nXfer, &ovZero);

  CurPtr.QuadPart = sizeof(RECORD)*atoi(argv[2])+sizeof(HEADER);

  FPos = SetFilePointer(hFile, CurPtr.LowPart, &CurPtr.HighPart, FILE_BEGIN);

  if (FPos == 0xFFFFFFFF && GetLastError() != NO_ERROR) ReportError(_T("Ошибка указателя."), 4, TRUE);

  SetEndOfFile(hFile);

 }

 /* Считать заголовок файла: определить количество записей и количество непустых записей. */

 ReadFile(hFile, &Header, sizeof(HEADER), &nXfer, &ovZero);

 /* Предложить пользователю считать или записать запись с определенным номером. */

 while(TRUE) {

  HeaderChange = FALSE;

  RecordChange = FALSE;

  _tprintf(_Т("Введите r(ead)/w(rite)/d(elete)/q Запись#\n"));

  _tscanf(_T("%c" "%d" "%c"), &Command, &RecNo, &Extra );

  if (Command == 'q') break;

  CurPtr.QuadPart = RecNo * sizeof(RECORD) + sizeof(HEADER);

  ov.Offset = CurPtr.LowPart;

  ov.OffsetHigh = CurPtr.HighPart;

  ReadFile(hFile, &Record, sizeof(RECORD), &nXfer, &ov);

  GetSystemTime(&CurrentTime); /* Обновить поля даты и времени в записи. */

  Record.RecordLastRefernceTime = CurrentTime;

  if (Command == 'r' || Command == 'd') { /*Вывести содержимое записи.*/

   if (Record.ReferenceCount == 0) {

    _tprintf(_T("Запись номер %d – пустая.\n"), RecNo);

    continue;

   } else {

    _tprintf(_Т("Запись номер %d. Значение счетчика: %d \n"), RecNo, Record.ReferenceCount);

    _tprintf(_Т("Данные: %s\n"), Record.DataString);

    /* Упражнение: вывести метки времени. См. следующий пример. */

    RecordChange = TRUE;

   }

   if (Command == 'd') { /* Удалить запись. */

    Record.ReferenceCount = 0;

    Header.NumNonEmptyRecords--;

    HeaderChange = TRUE;

    RecordChange = TRUE;

   }

  } else if (Command == 'w') { /* Записать данные. Впервые? */

   _tprintf(_Т("Введите новую строку для записи.\n"));

   _getts(String);

   if (Record.ReferenceCount == 0) {

    Record.RecordCreationTime = CurrentTime;

    Header.NumNonEmptyRecords++;

    HeaderChange = TRUE;

   }

   Record.RecordUpdateTime = CurrentTime;

   Record.ReferenceCount++;

   _tcsncpy(Record.DataString, String, STRING_SIZE-1);

   RecordChange = TRUE;

  } else {

   _tprintf(_T("Допустимые команды: r, w и d. Повторите ввод.\n"));

  }

  /* Обновить запись на месте, если ее содержимое изменилось. */

  if (RecordChange) WriteFile(hFile, &Record, sizeof(RECORD), &nXfer, &ov);

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

  if (HeaderChange) WriteFile(hFile, &Header, sizeof(Header), &nXfer, &ovZero);

 }

 _tprintf(_T("Вычисленное количество непустых записей: %d\n"), Header.NumNonEmptyRecords);

 CloseHandle(hFile);

 return 0;

Атрибуты файлов и управление каталогами

Существует возможность просмотра указанного каталога с целью поиска файлов и других каталогов, имена которых соответствуют заданному шаблону, одновременно с получением атрибутов файлов. Для выполнения поиска требуется дескриптор поиска (search handle), получаемый с помощью функции FindFirstFile. Для нахождения файлов, имена которых удовлетворяют заданным условиям, используется функция FindNextFile, а для прекращения поиска — функция FindClose.