Вывод и ввод данных в файл с помощью функций библиотеки периода выполнения (C Runtime Library), страница 3

   //===== Методы управления файлом

bool Open();

bool Close();

bool Seek(long pos);

bool Read(DWORD nBytes, void* data);

bool Write(DWORD nBytes, void* data);

bool Delete();

};

MyFile::MyFile (string name)

{

   //=== Создаем файл заново, уничтожая возможно уже существующий

m_hFile = CreateFile(name.c_str(),GENERIC_READ | GENERIC_WRITE,

  FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,0);

m_bOpen = m_hFile != INVALID_HANDLE_VALUE;  // Получилось ли?

m_Name = m_bOpen ? name : "";

}

MyFile::~MyFile() { Close(); Delete(); }

bool MyFile::Open()

{

   //==== Открываем  уже существующий файл как для записи, так и для чтения

m_hFile =  CreateFile(m_Name.c_str(),GENERIC_READ | GENERIC_WRITE,

  FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0);

return m_bOpen = m_hFile != INVALID_HANDLE_VALUE;

}

bool MyFile::Close() //=== Закрываем файл

{

if (m_bOpen)

  m_bOpen = CloseHandle (m_hFile) == 0;

return !m_bOpen;

}

bool MyFile::Seek(long pos) // Позиционируем файловый указатель

{

return SetFilePointer(m_hFile, pos, 0, FILE_BEGIN) != 0xffffffff;

}

bool MyFile::Read(DWORD nBytes, void* data)

{

   //====== Читаем из файла   nBytes    байт

DWORD n;

return ReadFile (m_hFile, data, nBytes, &n, 0) == TRUE && n == nBytes;

}

bool MyFile::Write(DWORD nBytes, void* data)

{

DWORD n;

return WriteFile (m_hFile, data, nBytes, &n, 0) == TRUE && n == nBytes;

}

bool MyFile::Delete()

{

return DeleteFile (m_Name.c_str()) == 0;

}

void main()

{

   //=== Объявляем и инициализируем строки текста

string s = "She sells sea shells on the sea shore",

   msg = "It works!";

DWORD len = s.size();

   //=== Создаем новый файл и сразу же открываем его

MyFile file(string("Test.dat"));

if (file.m_bOpen)

{

  cout << "\nCreated file: Test.dat";

  if (file.Write (len, (void*)s.c_str()))

  {

   cout << "\nWrote to file " << len << " bytes:\n\n  \'" << s << "\'\n";

   if (file.Close())

   {

    cout << "\nClosed file";

    s.assign(20,' ');  // Стираем строку (заполняем  пробелами)

    if (file.Open() && file.Read(20, (void*)s.c_str()))

    {

      cout << "\nOpened again and read 20 bytes:\n\n  \'" << s << "\'\n";

      //=== Показываем как найти желаемую позицию в файле

      if (file.Seek(10))

      {

        cout << "\nFound 10 pos";

        s.assign(len-10,' ');    // Стираем последние 10 символов строки s

        if (file.Read(len-10, (void*)s.c_str()))

            cout << "\nHas read beginning from it:\n\n  \'" << s << "\'\n";

        else

         msg = "Could not read";        

      }

      else

        msg = "Could not seek the position";

    }

    else

      msg = "Could not read";

   }

   else

    msg = "Could not close file";

  }

  else

  msg = "Could not write";

}

else

  msg = "Could not create";

file.Close();

MessageBox (0, msg.c_str(), "Message", MB_OK);

cout << "\n\n";

}

Комментарии в тексте должны помочь вам в осмыслении алгоритма записи, чтения и позиционирования. Пользуйтесь также MSDN. Введите изменения (такие, чтобы спровоцировать ошибки). Например, попробуйте найти в файле позицию (­–10). Она не существует. Отметьте, что установка файлового указателя за пределы конца файла (например, в позицию 10000) не считается ошибкой (См. MSDN по теме SetFilePointer).

Позиционирование в файле

Вновь вернемся к библиотеке stdio.h. Рассмотрим как можно перемещать файловый указатель, или как найти в файле нужное место. Для этой цели можно использовать функцию fseek. Она позволяет сдвинуться на указанное число байт либо от текущей позиции, либо от любого из концов файла. Существует также функция rewind, которая переустанавливает указатель файла на начало потока. Тот же результат будет достигнут, если вызвать функцию позиционирования fseek(fp,0L,SEEK_SET); за исключением того, что она дополнительно очищает флаги ошибок. Смысл параметров надо всегда искать в MSDN, так как нет никакой возможности запомнить типы и количество параметров всех используемых вами функций и методов классов.

Необходимо помнить, что если файл открыт в режиме update (и запись, и чтение), то вывод не может производиться сразу после ввода. Между этими операциями следует выполнить fseek или rewind. Для выяснения текущей позиции файлового указателя можно использовать функцию ftell, которая возвращает номер позиции относительно начала файла. Некоторая тонкость ее использования состоит в том, что при записи файла в текстовом режиме вместо пары символов возврат каретки-перевод строки (\r\n) записывается один символ с кодом 10. Поэтому позиция символа в файле численно может не совпадать с его позицией в строке текста.

Следующая программа из пяти модулей (функций) моделирует запись и чтение отдельных фрагментов текста компьютерного опросника или какой-то другой обучающей программы. Она сначала записывает в файл story.txt строку текста, состоящую из предложений (фрагментов). Затем функция Scan сканирует файл и запоминает позиции начал каждого из фрагментов (в массиве long pos[];). При этом мы используем символическую константу EOQ (End Of Question), с помощью которой вы можете задать код специального символа, разделяющего текстовые фрагменты (у нас это — точка). Далее с помощью функции ReadText мы читаем все фрагменты, но в обратном порядке, чтобы проиллюстрировать свободу в управлении позиционированием в файле. Вспомогательные функции YesNo и ShowError упрощают обработку различных исключительных ситуаций, которые неплохо предусмотреть заранее. Для удобства работы с текстом мы используем глобальный массив char buff[BUFFSIZE]; который служит буфером (местом временного хранения текста).