На этом шаге мы рассмотрим средства, используемые для чтения из файла и запись в файл.
Для поддержки операций прямого небуферизованного чтения и записи в дисковые файлы в CFile существуют функции Read() и Write(), которые обращаются к ReadFile() и WriteFile() - стандартным функциям Microsoft Windows API. Поскольку прямой доступ к файлу - дело весьма непростое (например, буферы и смещения указателей надо задавать в единицах, кратных размеру сектора дискового тома), в библиотеке времени выполнения существуют функции потокового ввода/вывода (stream I/O).
Они позволяют работать с любым форматом данных дисковых файлов: от отдельных символов до больших структур данных. Также эти функции поддерживают буферизацию ввода/вывода, что иногда позволяет улучшить быстродействие системы. Вот несколько членов этого семейства: fopen(), fseek(), fread() и fwrite(). Так как Вы программируете на C++, Вы наверняка знакомы с потоковым вводом/выводом с применением классов iostream.
В MFC потоковый ввод/вывод обеспечивается классом CStdioFile. Его функции-члены Read() и Write() обращаются к библиотечным функциям потокового ввода/вывода. Всегда пользуйтесь гибким и простым в применении CStdioFile, кроме тех случаев, когда Вам требуется прямой низкоуровневый доступ к файлу.
Файлы, связанные с объектом CStdioFile, открываются либо в текстовом, либо в двоичном режиме. В первом случае поддерживается специальная обработка пары символов "перевод каретки/перевод строки" (CR/LF). В этом режиме символ новой строки (0х0А), передаваемый объекту CStdioFile, записывается в файл как пара байт (0x0D, 0х0А), соответствующих CR/LF, а при считывании эта пара преобразуется обратно в один байт 0х0А. Чтобы открыть объект CStdioFile как текстовый файл, в функцию Ореn() нужно передать флаг CFile::typeText следующим образом:
CStdioFile inFile("MyFile.txt", CFile::modeRead | CFile::typeText);
Для задания двоичного режима служит флаг CFile::typeBinary. В этом случае символы новой строки никак не преобразуются.
Для чтения данных из файлового объекта CStdioFile предназначены две функции: Read() и ReadString().
В первую передается указатель на буфер, в который будут помещаться данные, полученные в процессе чтения, а также целое беззнаковое значение (типа UINT), задающее число байт, которые требуется считать. Read() возвращает число считанных байт. Если из-за доcтижения конца файла заданное число байт получить не удается, функция возвратит число реально считанных байт.
Ошибка чтения заставляет CFileException возбудить исключение. При записи в текстовом режиме пользуйтесь функцией ReadString(), которая почти полностью идентична Read(). Отличие ее вот в чем:
ReadString() позволяет считать отдельную строку текстового файла и возвращает булево значение, указывающее на успех или неудачу операции.
Write() похожа на Read() тем, что также имеет два параметра: буфер с записываемой информацией и число байт, которые требуется записать. Но Write() не возвращает количество записанных байт. При сбое (им считается и невозможность записать все указанные байты) CFileException генерирует исключение. При записи в текстовом режиме пользуйтесь функцией CStdioFile::WriteString(), который позволяет вставлять символ новой строки.
Приведенный ниже код иллюстрирует, как использовать класс CStdioFile для записи и чтения дисковых файлов. Программа открывает файл MyFiIe.bin в двоичном режиме и считывает его блоками по 10 байт. Каждый блок записывается в новой строке специально созданного файла Output.txt:
try { CStdioFile inFile("MyFile.bin", CFile::modeRead | CFile::typeBinary); CStdioFile outFile("outfile.txt", CFile::modeCreate | CFile::modeWrite | CFile::typeText); const UINT linelength = 10; TCHAR strBuf[16]; while(inFile.ReadString(strBuf, linelength)) { _tcscat(strBuf, _T("\n")); outFile.WriteString(strBuf); } catch(CFileException * fx) TCHAR buf[255]; fx->GetErrorMessage(buf, 255); AfxMessageBox(buf); }
На следующем шаге мы рассмотрим прямой доступ к файлу.