Запись и считывание данных (работа с файлами)

Запись и считывание данных (работа с файлами)

В этой работе будут рассмотрены два способа работы с файлами и стандартный класс MFC CFileDialog.

1. Работа с файлами в C ( работает и в C++ ).

2. Работа с файлами с помощью MFC( классы CFile, CStdioFile, ... ) и стандартный класс MFC CFileDialog.

1. Работа с файлами в C ( работает и в C++ )..

#include <iostream.h>

#include <stdio.h>

void main( void ) 

{

FILE *file;

char* file_name = "file.txt";

char load_string[50] = "none";

file = fopen( file_name, "w" ); 

fputs( "string", file );

fclose( file );

file = fopen( file_name, "r" );

if( file != 0 )

{

fgets( load_string, 50 , file ); 

cout << "load_string = " << load_string << endl;

}

else

{

cout << "File not found !!!" << endl;

}

fclose(file);

}

Описание функций работы с файломи находятся в библиотеке stdio.h

Сначала надо создать указатель на переменную типа FILE ( FILE* file; ).

Открытие файла производится вызовом функции fopen ( file = fopen( file_name, "w" ); )

Первый параметр этой функции - имя файла, второй - указывает в каком режиме должен быть открыт файл. "w" - открыть для записи, "r" - открыть для чтения, "a" - дополнение файла( это наиболее используемые режимы, хотя есть и другие ). Запись и считывание данных из файла осуществляется следующими функциями : fputc, fputs, fgetc, fgets, fprintf, fscanf( описание этих функций смотрите в stdio.h).

Закрытие файла осуществляется вызовом функции fclose ( fclose( file ); ).

Работа с файлами с помощью MFC( классы CFile, CStdioFile, ... ) и стандартный класс MFC CFileDialog.

Класс CFile 

Класс CMemFile 

Класс CStdioFile 

Примеры записи и чтения из файла

В библиотеку MFC включено несколько классов для обеспечения работы с файлами. Рассматриваемые ниже классы наследуются от базового класса CFile. 

Класс CFile

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

Чтобы получить доступ к файлу, сначала надо создать объект класса CFile. Конструктор класса позволяет сразу после создания такого объекта открыть файл. Но можно открыть файл и позднее, воспользовавшись методом Open.

Открытие и создание файлов

После создания объекта класса CFile можно открыть файл, вызвав метод Open. Методу надо указать путь к открываемому файлу и режим его использования. Прототип метода Open имеет следующий вид:

virtual BOOL Open(LPCTSTR lpszFileName,

UINT nOpenFlags,    CFileException* pError=NULL); 

В качестве параметра lpszFileName надо указать имя открываемого файла. Можно указать только имя файла или полное имя файла, включающее полный путь к нему.

Второй параметр nOpenFlags определяет действие, выполняемое методом Open с файлом, а также атрибуты файла. Ниже представлены некоторые возможеые значения параметра nOpenFlags:

CFile::modeCreate - Создается новый файл. Если указанный файл существует, то его содержимое стирается и длина файла устанавливается равной нулю. 

CFile::modeNoTruncate - Этот файл предназначен для использования совместно с файлом CFile::modeCreate. Если создается уже существующий файл, то его содержимое не будет удалено. 

CFile::modeRead - Файл открывается только для чтения. 

CFile::modeReadWrite - Файл открывается для записи и для чтения. 

CFile::modeWrite - Файл открывается только для записи. 

CFile::typeText - Используется классами, порожденными от класса CFile, например CStdioFile, для работы с файлами в текстовом режиме. Текстовый режим обеспечивает преобразование комбинации символа возврата каретки и символа перевода строки. 

CFile::Binary - Используется классами, порожденными от класса CFile, например CStdioFile, для работы с файлами в двоичном режиме.

Необязательный параметр pError, который является указателем на объект класса CFileException, используется только в том случае, если выполнение операции с файлом вызовет ошибку. При этом в объект, указываемый pError, будет записана дополнительная информация.

Метод Open возвращает не нулевое значение, если файл открыт и нуль в случае ошибки. Ошибка при открытии файла может случиться, например, если методу Open указан для чтения несуществующий файл.

Идентификатор открытого файла

В состав класса CFile входит элемент данных m_hFile типа UINT. В нем хранится идентификатор открытого файла. Если объект класса CFile уже создан, но файл еще не открыт, то в переменной m_hFile записана константа hFileNull.

Обычно идентификатор открытого файла непосредственно не используется. Методы класса CFile позволяют выполнять практически любые операции с файлами и не требуют указывать идентификатор файла. Так как m_hFile является элементом класса, то реализация его методов всегда имеет свободный доступ к нему.

Закрытие файлов

После завершения работы с файлом, его надо закрыть. Класс CFile имеет для этого специальный метод Close. Нужно заметить, что если был создан объект класса CFile и открыт файл, а затем объект удаляется, то связанный с ним файл закрывается автоматически с помощью деструктора.

Чтение и запись файлов

Для доступа к файлам предназначено несколько методов класса CFile: Read, ReadHuge, Write, WriteHuge, Flush. Методы Read и ReadHuge предназначены для чтения данных из предварительно открытого файла. В 32-разрядных операционных системах оба метода могут одновременно считать из файла больше 65535 байт. Спецификация ReadHuge считается устаревшей и оставлена только для совместимости с 16-разрядными операционными системами.

Данные, прочитанные из файла, записываются в буфер lpBuf. Параметр nCount определяет количество байт, которое надо считать из файла. Фактически из файла может быть считано меньше байт, чем запрошено параметром nCount. Это происходит, если во время чтения достигнут конец файла. Методы возвращают количество байт, прочитанных из файла.

Для записи в файл предназначены методы Write и WriteHuge. В 32-разрядных операционных системах оба метода могут одновременно записывать в файл больше 65535 байт. Методы записывает в открытый файл nCount байт из буфера lpBuf. В случае возникновения ошибки записи, например переполнения диска, методы вызывает обработку исключения.

Метод Flush

Когда используется метод Write или WriteHuge для записи данных на диск, они некоторое время могут находиться во временном буфере. Чтобы убедиться, что необходимые изменения внесены в файл на диске, нужно воспользоваться методом Flush.

Операции с файлами

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

Для изменения имени файла класс CFile включает статический метод Rename, выполняющий функции этой команды. Метод нельзя использовать для переименования каталогов. В случае возникновения ошибки метод вызывает исключение.

Для удаления файлов в классе CFile включен статический метод Remove, позволяющий удалить указанный файл. Этот метод не позволяет удалять каталоги. Если удалить файл невозможно, то метод вызывает исключение.

Чтобы определить дату и время создания файла, его длину и атрибуты, предназначен статический метод GetStatus. Существует две разновидности метода - первый определен как виртуальный, а второй - как статический метод.

Виртуальная версия метода GetStatus определяет состояние открытого файла, связанного с данным объектом класса CFile. Этот метод вызывается только тогда, когда объект класса CFile создан и файл открыт.

Статическая версия метода GetStatus позволяет определить характеристики файла, не связанного с объектом класса CFile. Чтобы воспользоваться этим методом, необязательно предварительно открывать файл.

Блокировка

В состав класса включены методы LockRange и UnlockRange, позволяющие заблокировать один или несколько фрагментов данных файла для доступа из других процессов. Если приложение пытается повторно блокировать данные, уже заблокированные раньше этим или другим приложением, вызывается исключение. Блокировка представляет собой один из механизмов, позволяющих нескольким приложениям или процессам одновременно работать с одним файлом, не мешая друг другу.

Установить блокировку можно с помощью метода LockRange. Чтобы снять установленные блокировки, надо воспользоваться методом UnlockRange. Если в одном файле установлены несколько блокировок, то каждая из них должна сниматься отдельным вызовом метода UnlockRange.

Позиционирование

Чтобы переместить указатель текущей позиции файла в новое положение, можно воспользоваться одним из следующих методов класса CFile - Seek, SeekToBegin, SeekToEnd. В состав класса CFile также входят методы, позволяющие установить и изменить длину файла, - GetLength, SetLength.

При открытии файла указатель текущей позиции файла находится в самом начале файла. Когда порция данных прочитана или записана, то указатель текущей позиции перемещается в сторону конца файла и указывает на данные, которые будут читаться или записываться очередной операцией чтения или записи в файл.

Чтобы переместить указатель текущей позиции файла в любое место, можно воспользоваться универсальным методом Seek. Он позволяет переместить указатель на определенное число байт относительно начала, конца или текущей позиции указателя. 

Чтобы переместить указатель в начало или конец файла, наиболее удобно использовать специальные методы. Метод SeekToBegin перемещает указатель в начало файла, а метод SeekToEnd - в его конец.

Но для определения длины открытого файла совсем необязательно перемещать его указатель. Можно воспользоваться методом GetLength. Этот метод также возвращает длину открытого файла в байтах. Метод SetLength позволяет изменить длину открытого файла. Если при помощи этого метода размер файла увеличивается, то значение последних байт не определено.

Текущую позицию указателя файла можно определить с помощью метода GetPosition. Возвращаемое методом GetPosition 32-разрядное значение определяет смещение указателя от начала файла.

Характеристики открытого файла

Чтобы определить расположение открытого файла на диске, надо вызвать метод GetFilePath. Этот метод возвращает объект класса CString, в котором содержится полный путь файла, включая имя диска, каталоги, имя файла и его расширение.

Если требуется определить только имя и расширение открытого файла, можно воспользоваться методом GetFileName. Он возвращает объект класса CString, в котором находится имя файла. В случае, когда нужно узнать только имя открытого файла без расширения, пользуются методом GetFileTitle.

Следующий метод класса CFile позволяет установить путь файла. Это метод не создает, не копирует и не изменяет имени файла, он только заполняет соответствующий элемент данных в объекте класса CFile.

Класс CMemFile

В библиотеку MFC входит класс CMemFile, наследуемый от базового класса CFile. Класс CMemFile представляет файл, размещенный, в оперативной памяти. С объектами класса CMemFile так же, как и с объектами класса CFile. Отличие заключается в том, что файл, связанный с объектом CMemFile, расположен не на диске, а в оперативной памяти компьютера. За счет этого операции с таким файлом происходят значительно быстрее, чем с обычными файлами.

Работая с объектами класса CMemFile, можно использовать практически все методы класса CFile, которые были описаны выше. Можно записывать данные в такой файл или считывать их. Кроме этих методов в состав класса CMemFile включены дополнительные методы.

Для создания объектов класса CMemFile предназначено два различных конструктора. Первый конструктор CMemFile имеет всего один необязательный параметр nGrowBytes:

CMemFile(UINT nGrowBytes=1024); 

Этот конструктор создает в оперативной памяти пустой файл. После создания файл автоматически открывается (не нужно вызывать метод Open).

Когда начинается запись в такой файл, автоматически выделяется блок памяти. Для получения памяти методы класса CMemFile вызывают стандартные функции malloc, realloc и free. Если выделенного блока памяти недостаточно, его размер увеличивается. Увеличение блока памяти файла происходит по частям по nGrowBytes байт. После удаления объекта класса CMemFile используемая память автоматически возвращается системе.

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

CMemFile(BYTE* lpBuffer, UINT nBufferSize, 

UINT nGrowBytes=0); 

Параметр lpBuffer указывает на буфер, который будет использоваться для файла. Размер буфера определяется параметром nBufferSize.

Необязательный параметр nGrowBytes используется более комплексно, чем в первом конструкторе класса. Если nGrowBytes содержит нуль, то созданный файл будет содержать данные из буфера lpBuffer. Длина такого файла будет равна nBufferSize.

Если nGrowBytes больше нуля, то содержимое буфера lpBuffer игнорируется. Кроме того, если в такой файл записывается больше данных, чем помещается в отведенном буфере, то его размер автоматически увеличивается. Увеличение блока памяти файла происходит по частям по nGrowBytes байт.

Класс CMemFile позволяет получить указатель на область памяти, используемую файлом. Через этот указатель можно непосредственно работать с содержимым файла, не ограничивая себя методами класса CFile. Для получения указателя на буфер файла можно воспользоваться методом Detach. Перед этим полезно определить длину файла (и соответственно размер буфера памяти), вызвав метод GetLength.

Метод Detach закрывает данный файл и возвращает указатель на используемый им блок памяти. Если опять потребуется открыть файл и связать с ним оперативный блок памяти, нужно вызвать метод Attach.

Нужно отметить, что для управления буфером файла класс CMemFile вызывает стандартные функции malloc, realloc и free. Поэтому, чтобы не нарушать механизм управления памятью, буфер lpBuffer должен быть создан функциями malloc или calloc.

Класс CStdioFile

Тем, кто привык пользоваться функциями потокового ввода/вывода из стандартной библиотеки C и C++, следует обратить внимание на класс CStdioFile, наследованный от базового класса CFile. Этот класс позволяет выполнять буферизированный ввод/вывод в текстовом и двоичном режиме. Для объектов класса CStdioFile можно вызывать практически все методы класса CFile.

В класс CStdioFile входит элемент данных m_pStream, который содержит указатель на открытый файл. Если объект класса CStdioFile создан, но файл еще не открыт, либо закрыт, то m_pStream содержит константу NULL.

Класс CStdioFile имеет три различных конструктора. Первый конструктор класса CStdioFile не имеет параметров. Этот конструктор только создает объект класса, но не открывает никаких файлов. Чтобы открыть файл, надо вызвать метод Open базового класса CFile.

Второй конструктор класса CStdioFile можно вызвать, если файл уже открыт и нужно создать новый объект класса CStdioFile и связать с ним открытый файл. Этот конструктор можно использовать, если файл был открыт стандартной функцией fopen. Параметр метода должен содержать указатель на файл, полученный вызовом стандартной функции fopen.

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

Для чтения и записи в текстовый файл класс CStdioFile включает два новых метода: ReadString и WriteString. Первый метод позволяет прочитать из файла строку символов, а второй метод - записать.

Примеры записи и чтения из файла

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

Открытие файла и чтение из него

CString m_Text;  ……  // создание стандартной панели

выбора файла Open 

CFileDialog DlgOpen(TRUE,(LPCSTR)"txt",NULL,

OFN_HIDEREADONLY,(LPCSTR)" 

Text Files (*.txt) |*.txt||");  

// отображение стандартной панели выбора файла Open 

if(DlgOpen.DoModal()==IDOK)  { 

// создание объекта и открытие файла для чтения 

CStdioFile File(DlgOpen.GetPathName(),

    CFile::modeRead|CFile::typeBinary);  

// чтение из файла строки 

CString& ref=m_Text;   File.ReadString(ref<

FONT FACE="Courier New">);

// передается ссылка на строку m_Text  } 

Здесь находится рабочий код программы, выполненной для простоты в виде консольного приложения под MFC. Чтобы программа работала, не забудте сделать следующее:

Запустите программу - Build / Rebuild all ( будут ошибки ), выберите Build / Set active configuration - Win 32 Realise, выберите пункт меню "Project", далее "Settings...", закладку "C/C++", Category - Code Generation и в пункте "Use run-time library" выберите "Multithreaded". После этого сделайте опять Build / Rebuild all и программа будет работать.

Открытие файла и запись из него

CString m_Text;  ……  // создание стандартной панели 

выбора файла SaveAs 

CFileDialog DlgSaveAs(FALSE,(LPCSTR)"txt",NULL,

OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, 

(LPCSTR)" Text Files (*.txt) |*.txt||"); 

// отображение стандартной панели выбора

        файла SaveAs 

if(DlgSaveAs.DoModal()==IDOK)  { 

// создание объекта и открытие файла для записи

CStdioFile File(DlgSaveAs.GetPathName(),

 CFile::modeCreate|CFile::modeWrite|

                   CFile::typeBinary);  

// запись в файл строки 

File.WriteString((LPCTSTR)m_Text);  } 

Здесь находится рабочий код программы, выполненной для простоты в виде консольного приложения под MFC. Чтобы программа работала, не забудте сделать следующее:

Запустите программу - Build / Rebuild all ( будут ошибки ), выберите Build / Set active configuration - Win 32 Realise, выберите пункт меню "Project", далее "Settings...", закладку "C/C++", Category - Code Generation и в пункте "Use run-time library" выберите "Multithreaded". После этого сделайте опять Build / Rebuild all и программа будет работать.