Работа с двоичными файлами с использованием функций WinApi

В начало

Создание нового файла

Для создания файла используется функция WinApi CreateFile().

System::AnsiString vasFileName;
HANDLE hFile;
hFile = CreateFile(
        vasFileName.c_str(),        // имя файла, преобразуемое к типу char*
        GENERIC_READ|GENERIC_WRITE, // доступ для чтения и записи
        0,                          // файл не может быть разделяемым
        NULL,                       // дескриптор файла не наследуется
        CREATE_NEW,                 // создать новый если не существует
        FILE_ATTRIBUTE_READONLY,    // файл имеет атрибут "только для чтения"
        NULL                        // всегда NULL для Windows
); 
if(hFile != INVALID_HANDLE_VALUE)
{                           //Файл создан
 CloseHandle(hFile);
 return;
}else
{                           //Файл не создан
 CloseHandle(hFile);      
 //Здесь можно поместить сообщение об ошибке
 return;
}

FILE_ATTRIBUTE определены следующим образом:

#define FILE_ATTRIBUTE_READONLY             0x00000001  
#define FILE_ATTRIBUTE_HIDDEN               0x00000002  
#define FILE_ATTRIBUTE_SYSTEM               0x00000004  
#define FILE_ATTRIBUTE_DIRECTORY            0x00000010  
#define FILE_ATTRIBUTE_ARCHIVE              0x00000020  
#define FILE_ATTRIBUTE_DEVICE               0x00000040  
#define FILE_ATTRIBUTE_NORMAL               0x00000080  
#define FILE_ATTRIBUTE_TEMPORARY            0x00000100  
#define FILE_ATTRIBUTE_SPARSE_FILE          0x00000200  
#define FILE_ATTRIBUTE_REPARSE_POINT        0x00000400  
#define FILE_ATTRIBUTE_COMPRESSED           0x00000800  
#define FILE_ATTRIBUTE_OFFLINE              0x00001000  
#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED  0x00002000  
#define FILE_ATTRIBUTE_ENCRYPTED            0x00004000  

С помощью этой функции можно открывать уже существующие файлы и консоли для консольных приложений, усекать их, открывать каталоги. Это все задается пятым параметром. Для создания файла этот параметр задается как CREATE_NEW. Вновь создаваемый файл открывается как для чтения, так и для записи, о чем свидетельствует второй параметр функции. Третий и четвертый параметры редко имеют какое-либо практическое применение в Windows 9x и требуются в основном при создании систем с разделением доступа на базе Windows NT. Шестой параметр определяет, какие атрибуты будут установлены для создаваемого файла. В данном случае будет присвоен атрибут Read-Only, позволяющий только чтение файла без записи в него. Windows самостоятельно закрывает все файлы и освобождает дескрипторы, как только завершается выполнение программы, но правило чистоты программирования требует не заставлять думать систему, когда сам знаешь как выполнить то или иное действие.

В начало

Открытие существующего файла

Функция WinApi OpenFile() открывает файл. Помимо этого, OpenFile() умеет создавать и удалять файлы. Во многом назначения этой функции пересекаются с CreateFile(), и существует в последних версиях WinApi для совместимости с ранними версиями Windows. Поэтому везде, где это возможно, лучше пользовать CreateFile().

System::AnsiString vasFileName="a.000";
HFILE              hFile;
OFSTRUCT           tOfStr;
tOfStr.cBytes = sizeof tOfStr;
hFile = OpenFile(
        vasFileName.c_str(), //имя файла, преобразуемое к типу char*
        &tOfStr,             //указатель на буфер с информацией о файле
        OF_READ);            // файл открыт для чтения
if(hFile != HFILE_ERROR)
{                             //Файл открыт
 CloseHandle(HANDLE(hFile));
 return;
}
else
{
 CloseHandle(HANDLE(hFile));
 //Здесь может быть помещено сообщение об ошибке
 return;
}

Функция OpenFile() принимает всего три аргумента: имя открываемого файла, указатель на структуру OFSTRUCT и флаг режима открытия файла. Структура OFSTRUCT заполняется данными об открытом файле. Она предоставляет такую информацию, как свойства файла, его размер и т. д.

typedef struct _OFSTRUCT 
{
 BYTE cBytes;
 BYTE fFixedDisk;
 WORD nErrCode;
 WORD Reserved1;
 WORD Reserved2;
 CHAR szPathName[OFS_MAXPATHNAME];
}OFSTRUCT, *LPOFSTRUCT, *POFSTRUCT;

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

  • OF_CANCEL

  • OF_CREATE

  • OF_DELETE

  • OF_EXIST

  • OF_PARSE

  • OF_PROMPT

  • OF_READ

  • OF_READWRITE

  • OF_REOPEN

  • OF_SHARE_COMPAT

В начало

Удаление файлов

Для удаления файлов используется функция WinApi DeleteFile().

System::AnsiString vasFileName;
DeleteFile(
           vasFileName.c_str()); // имя удаляемого файла

DeleteFile() не удаляет защищенные от записи файлы (READONLY).

В начало

Копирование и перемещение файлов

Для копирования используется функция WinApi CopyFile, для перемещения MoveFile().

Определим имена файлов:

 System::AnsiString vasNameFileFrom;
 System::AnsiString vasNameFileTo;

Функция CopyFile():

if(CopyFile(
    vasNameFileFrom.c_str(), // имя копируемого файла                
    vasNameFileTo.c_str(),   // имя нового файла
    TRUE))                   // если файл уже существует не копировать - true
 
{
 //Файл скопирован
}
else 
{
 //Сообщение об ошибке копирования
}

Аналогично CopyFile() действует функция API для переноса файла MoveFile().

if(MoveFile(
    vasNameFileFrom.c_str(), // имя переносимого файла                
    vasNameFileTo.c_str())   // имя нового файла

{
 //Файл перемещен
}
else 
{
 //Сообщение об ошибке переноса файла
}

Единственное отличие MoveFile() от CopyFile() - отсутствие третьего параметра, отвечающего за блокировку процесса переноса в случае, если файл уже существует.

В начало

Чтение информации из файла

Для чтения информации из файла используется функцию Win API ReadFile(), и ряд функций, обеспечивающих ее информацией:

HFILE              hFile;
//Имя читаемого файла
System::AnsiString vasFileName="a.000";
DWORD              vwCounter;
int                *virgRDWRBuff;
BY_HANDLE_FILE_INFORMATION bhFileInformation;
OFSTRUCT tOfStr;
tOfStr.cBytes = sizeof tOfStr;
//Открываем файл и заполняем информацией tOfStr
hFile = OpenFile(
 vasFileName.c_str(), // имя файла, преобразуемое к типу char*
 &tOfStr,             //указатель на буфер  с информацией о файле
 OF_READWRITE);       // файл открыт для чтения и записи
if(hFile == HFILE_ERROR)
{
 //Здесь может быть сообщение об ощибке
 CloseHandle(HANDLE(hFile));
return;
}
//Теперь можно получить информацию о файле
GetFileInformationByHandle
(
 HANDLE(hFile),     // дескриптор файла
 &bhFileInformation //адрес структуры, в которой сохраняется информация
);
//Резервируем память для всего файла обычно размер файла
//не больше  размера nFileSizeLow объявленного как DWORD
int viSize=bhFileInformation.nFileSizeLow/sizeof(int);
virgRDWRBuff = (int*) new int[viSize];
//Сдвигаем указатель на нужный байт - сдесь на начало
if(_llseek(hFile,0*sizeof(int),0) != (long)(0*sizeof(int)))
{
 CloseHandle(HANDLE(hFile));
 //Здесь может быть сообщение об ошибке
 return;
}
//Считать данные из файла
if(!ReadFile(HANDLE(hFile),virgRDWRBuff,
               (DWORD)viSize*sizeof(int),&vwCounter,NULL))
{
 //Здесь может быть сообщение об ошибке
}
else
{
 if(vwCounter == (DWORD)viSize*sizeof(int))
 {
  //Используем информацию
  int viRez=virgRDWRBuff[99];
 }else
 {
  //Здесь может быть сообщение об ошибке
 }
}
CloseHandle(HANDLE(hFile));
delete[] virgRDWRBuff;

В тексте объявляются две структуры. Первая, BY_HANDLE_FILE_INFORMATION, нужна для хранения полезной информации о файле. Вторая, OFSTRUCT требуется для работы функции API OpenFile(). Далее идет инициализация поля размера этой структуры.

Далее - создание буфера, в который будут скопированы данные, считанные из файла. Можно проделать расчет, основанный на размере файла, но можно поступить проще: задать размер принудительно, например 64 Кбайт. Для создания такого буфера можно использовать задание типа new char[0xfffe], выделяющей блок памяти подходящего размера. В конце обработчика необходимо освободить блок памяти операцией delete[].

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

Полученный дескриптор функцией OpenFile() открытого файла необходимо передать в качестве параметра для другой функции API - GetFileInformationByHandle(), которая заполняет структуру bhFileInformation типа BY_HANDLE_FILE_INFORMATION, переданную в качестве второго параметра, данными об открытом файле. В этой структуре имеются два поля, хранящие старшие и младшие четыре байта размера файла. Для малого файла хватает и младших четырех байтов.

Первые два параметра - это дескриптор читаемого файла и адрес буфера, в который будут считаны данные. Четвертый параметр функции - счетчик байтов, в который ReadFile() записывает количество байтов, считанных из файла.

Пятый параметр игнорируется.

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

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

В начало

Запись информации в файл

Функция WinApi WriteFile() записывает данные в файл. Она полностью аналогична по используемым параметрам функции ReadFile().

HFILE              hFile;
System::AnsiString vasFileName="a.000";
DWORD              vwCounter;
int                *virgRDWRBuff;
BY_HANDLE_FILE_INFORMATION bhFileInformation;
OFSTRUCT tOfStr;
tOfStr.cBytes = sizeof tOfStr;
//Открываем файл и заполняем информацией tOfStr
hFile = OpenFile(
 vasFileName.c_str(), // имя файла, преобразуемое к типу char*
 &tOfStr,             //указатель на буфер  с информацией о файле
 OF_READWRITE);       // файл открыт для чтения и записи
if(hFile == HFILE_ERROR)
{
 //Здесь может быть сообщение об ощибке
 CloseHandle(HANDLE(hFile));
return;
}
//Теперь можно получить информацию о файле, но понадобиться она 
//может только для того, чтобы не записать за пределы файла, хотя и 
//это не страшно, файл просто увеличится в размере
GetFileInformationByHandle
(
 HANDLE(hFile),     // дескриптор файла
 &bhFileInformation //адрес структуры, в которой сохраняется информация
);
//Резервировать память для всего файла обычно при записи нет необходимости
//так как известно сколько будем писать, например 100 чисел
int viSize=100;
virgRDWRBuff = (int*) new int[viSize];
//Сдвигаем указатель на нужный байт - сдесь на начало
if(_llseek(hFile,0*sizeof(int),0) != (long)(0*sizeof(int)))
{
 CloseHandle(HANDLE(hFile));
 //Здесь может быть сообщение об ошибке
 return;
}
//Пишем данные в буфер
for(int i=0; i < 100; i++) virgRDWRBuff[99-i]=i;
//Переносим содержимое буфера в файл
if(!WriteFile(HANDLE(hFile),virgRDWRBuff,
               (DWORD)viSize*sizeof(int),&vwCounter,NULL))
{
 //Здесь может быть сообщение об ошибке
}
else
{
 if(vwCounter == (DWORD)viSize*sizeof(int))
 {
  //Запись прошла успешно

 }else
 {
  //Здесь может быть сообщение об ошибке
 }
}
CloseHandle(HANDLE(hFile));
delete[] virgRDWRBuff;

В начало

Пример универсальной функции работы с файлами

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

//////////////////////////////////////////////////////////////////////
//     Функция чтения записи байт из (в) virgRDWRBuff[]
//                           или  из (в) vchrgRDWRBuf[]
//     int virgRDWRBuff[n]; и char vchrgRDWRBuf[n]; должны быть объявлены
//                                                  вне функции
// Функция принимает:         
//      имя директории                vasFileDir
//      имя файла                     vasFileName
//      режим чтения записи           viRegime 0 - чтение 1 - запись
//      смещение  в элементах int     viSeek  
//      число байт для записи/чтения  viNumber
//      что пишем                     viType   0 - целое 1 - char
/////////////////////////////////////////////////////////////////////
int        __fastcall
TForm1::iRDWR(AnsiString &vasFileDir,AnsiString vasFileName,
              int viRegime,int viSeek,int viNumber,int viType)
{
 HFILE       hFile;
 AnsiString  vasFileName=vasFileDir+"\\"+vasFileName;
 DWORD       dwCounter;
 OFSTRUCT    tOfStr;
 tOfStr.cBytes = sizeof tOfStr;
 // Открыть файл
 hFile = OpenFile(vasFileName.c_str(),&tOfStr,OF_READWRITE);
 if(hFile == HFILE_ERROR)
 {
  CloseHandle(HANDLE(hFile));
  //Сообщение об ошибке
  return 1;
 }
 BY_HANDLE_FILE_INFORMATION bhFileInformation;
 GetFileInformationByHandle
 (
  HANDLE(hFile), // Дескриптор файла
  // Адрес структуры, в которой сохраняется информация
  &bhFileInformation    
 );
 viRazmer=bhFileInformation.nFileSizeLow;
 //сдвинуться
 switch(viType)
 {
  case 0:
   if(_llseek(hFile,viSeek*sizeof(int),0) != (long)(viSeek*sizeof(int)))
   {
    CloseHandle(HANDLE(hFile));
    //Сообщение об ошибке
   return 1;
   }
   break;
  case 1:
   if(_llseek(hFile,viSeek*sizeof(char),0) != (long)(viSeek*sizeof(char)))
   {
    CloseHandle(HANDLE(hFile));
    //Сообщение об ошибке
   return 1;
   }
  break;
 }
 // считать (записать)данные из (в) файл
 switch(viRegime)
 {
  case 1:
   if(viType == 0)
   {
    if(!WriteFile(HANDLE(hFile),virgRDWRBuff,
                  (DWORD)(viNumber*sizeof(int)),&dwCounter,0))
    {
     //Сообщение об ошибке
     CloseHandle(HANDLE(hFile));
    return 1;
    }
   }else
   {
    if(!WriteFile(HANDLE(hFile),(char far*) vchrgRDWRBuf,
               (DWORD)(viNumber*sizeof(char)),&dwCounter,0))
    {
     //Сообщение об ошибке
     CloseHandle(HANDLE(hFile));
     return 1;
    }
   }//if(viType == 0)
  break;
  case 0:
   if(viType == 0)
   {
    if(!ReadFile(HANDLE(hFile),virgRDWRBuff,
                (DWORD)(viNumber*sizeof(int)),&dwCounter,0))
    {
     //Сообщение об ошибке
     CloseHandle(HANDLE(hFile));
     return 1;
    }
   }else
   {
    if(!ReadFile(HANDLE(hFile),(char far*) vchrgRDWRBuf,
               (DWORD)(viNumber*sizeof(char)),&dwCounter,0))
    {
     //Сообщение об ошибке
     CloseHandle(HANDLE(hFile));
     return 1;
    }
   }//if(viType == 0)
  break;
 }//switch(viRegime)
 CloseHandle(HANDLE(hFile));
return 0;
}

В начало

На главную подраздела

Домой