Примеры работы с файлами на С, С++

Определение: Файл - это именованный блок информации, расположенный на носителе информации.

Любой файл обладает следующим рядом особенностей:

  • Файл не может располагаться на диске непрерывно, однако пользователю файл предоставляется цельным блоком последовательной байтовой информации.
  • Название файла не может содержать символы: < > : " / \ |.
  • Большинство файлов обладает расширением - сочетанием символов, с помощью которых операционная система определяет тип файла. Расширение - необязательная часть.
  • У каждого файла есть, так называемые атрибуты, которые, например, определяют уровни доступа к нему. Используя атрибуты, операционная система узнает, как нужно и, главное, можно, работать с данным файлом.

Термины:

  • Дескриптор файла - уникальный номер, который операционная система присваивает любому открытому файлу, что бы отличать его от других. Когда файл закрывается, система "отбирает" у него дескриптор. Именно это уникальное число мы будем использовать для работы с конкретным файлом в наших программах.
  • Файловый указатель - специальная переменная, которая автоматически присваивается открытому файлу и хранит текущую позицию в файле. Она перемещается по файлу в момент процессов записи и чтения. Для большего понимания, вы можете представить данную переменную в виде курсора в любом текстовом редакторе.

Существует несколько разновидностей файлов. В Windows таких разновидностей две: текстовый файл и бинарный (двоичный) файл.

Файл можно открыть в текстовом или в двоичном режиме. Внимание, не следует это отождествлять с текстовым и двоичным форматами!!! Разница между режимами открытия состоит в том, что при открытии файла в двоичном режиме информация будет в том же виде, в котором она хранится на диске или в памяти. А, вот, при текстовом режиме символ окончания строки (\n) будет заменяться на двойное сочетание (\r\n). Это как обычно связано с "нестыковкой" систем MS DOS и Windows.

Функции для работы с содержимым файла библиотеки языка C

FILE *fopen(const char *filename, const char *mode)

Функция открывает файл.

filename - путь к файлу

mode - тип доступа

r - чтение, если файла нет, то данная функция генерирует ошибку (возвращает 0) 
w - запись, если файла нет, то файл создаётся, если есть исходное содержимое удаляется 
a - добавление в конец, если файла нет, то он создаётся 
r+ чтение и запись (файл должен существовать) 
w+ - чтение и запись (принцип работы как у w) 
a+ - добавление и чтение (принцип работы как у a)
Все вышеописанные режимы предназначены для текстового открытия файла. Для двоичного открытия перед режимом достаточно добавить букву b. Например, br.

Если функция отработала успешно, из неё возвращается указатель на открытый файл, в противном случае - нуль.

Указатель на открытый файл принято хранить в типе данных FILE*.

int fclose( FILE *stream )

Функция закрывает файл.

stream - указатель на закрываемый файл.

Если всё проходит успешно, то данная функция возвращает 0, или EOF в случае ошибки.

EOF (End Of File) - обозначение конца файла.

char *fgets( char *string, int n, FILE *stream )

Считывает строку начиная с текущей позиции.

Считывание останавливается:

…если был найден символ перехода на новую строчку ( он помещается в строку ) …если достигнут конец файла …если считано n-1 символов. string - cтрока, в которую попадают считанные данные

n - количество элементов в string

stream - указатель на открытый файл

Если всё прошло успешно функция возвращает считанную строку, если произошла ошибка или достигнут конец файла возвращается 0.

int fputs( const char *string, FILE *stream )

Записывает строку в файл, начиная с текущей позиции.

string - строка для записи

stream - указатель на открытый файл, куда производиться запись

Если функция отрабатывает успешно из неё возвращается неотрицательное значение. При ошибке возвращается EOF.


size_t fread( void *buffer, size_t size, size_t count, FILE *stream )

Функция считывает данные из файла в буфер.

buffer - адрес массива, куда запишутся данные

size - размер элемента массива в байтах

count - максимальное количество элементов для считывания

stream - указатель на открытый файл.

Функция возвращает количество считанных байт.

Примечание: Тип данных size_t определен в библиотеке stdio.h следующим образом: typedef unsigned int size_t;. Другими словами, это обычный беззнаковый int.


size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream )

Функция записывает массив данных в файл.

buffer - адрес массива, где содержатся данные

size - размер элемента массива в байтах

count - максимальное количество элементов для записи в файл

stream - указатель на открытый файл.

Функция возвращает количество записанных байт.


int feof( FILE *stream )

Функция проверяет достигнут ли конец файла.

stream - указатель на открытый файл

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


int _fileno( FILE *stream )

Данная функция возвращает дескриптор файла.

stream - указатель на открытый файл.


int fseek ( FILE *stream, int offset [, int whence] )

Устанавливает смещение в файле

stream - указатель на открытый файл

offset - смещение, измеряемое в байтах от начала файла

whence - точка, от которой производится смещение

SEEK_SET (0) - начало файла SEEK_CUR (1) - позиция текущего указателя файла SEEK_END (2) - конец файла (EOF) Функция возвращает значение 0, если указатель файла успешно перемещен, и ненулевое значение в случае неудачного завершения.


int _access( const char *path, int mode ) Функция определяет разрешения файла или директории.

  • path - путь к файлу или директории
  • mode - флаги для проверки
    00 - проверка на существование 
    02 - проверка на разрешение на запись 
    04 - проверка на разрешение на чтение 
    06 - проверка на чтение и запись

Если разрешение есть, функция возвращает ноль, в случае отсутствия -1.

Директории можно проверять только на существование.
//проверить существует объект или нет? Если нет выйти из программы.
		if (_access(str, 00)!=0){
			cout<<"Object \""<<str<<"\" does not exist."<<"\n";
			exit;
		}

long _filelength( int handle )

Возвращает размер файла в байтах.

handle - дескриптор файла.

В случае ошибки функция возвращает -1.

int _locking( int handle, int mode, long nbytes )

Блокирует или разблокирует байты файла начиная с текущей позиции в файле.

handle - дескриптор файла

mode - тип блокировки

_LK_LOCK - блокирует байты, если заблокировать байты не получается попытка повторяется через 1 секунду.Если после 10 попыток байты не заблокируются функция генерирует ошибку и возвращает -1 _LK_NBLCK - блокирует байты, если заблокировать байты не получается функция генерирует ошибку и возвращает -1 _LK_NBRLCK - то же самое, что и _LK_NBLCK _LK_RLCK - то же самое, что и _LK_LOCK _LK_UNLCK - разблокировка байт, которые были до этого заблокированы nbytes - количество байт для блокировки

Функция locking возвращает -1, если происходит ошибка и 0 в случае успеха.

Для работы этой функции кроме io.h, нужно подключить sys/locking.h

Функции для работы с файлами и директориям

stdio.h - переименование и удаление файлов.

rename (char * oldname, char * newname)

Переименовывает файл.

oldname - путь и текущее имя файла newname - путь и новое имя файла

Функция возвращает 0, если имя файла было успешно изменено, и ненулевое значение, если замена не произошла.

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

int remove(const char * filename) Удаляет файл.

filename - путь и имя файла

Функция возвращает 0, если имя файл был успешно удален, и ненулевое значение, если удаление не произошло.

Помните!!! В момент удаления файл должен быть закрыт. Кроме того, не забывайте, что удаление необратимо.

Установка прав на файл.

#include <sys/stat.h>
#include <io.h>

_chmod( "test.txt", _S_IREAD ); // установить атрибут только для чтения на файл test.txt
  • _findfirst(char * path, _finddata_t * fileinfo)

io.h - поиск файлов. Находит файлы по указанному пути, соответствующие маске.

Маска - строка, критерий поиска, содержащая символы * (любая последовательность любых символов) и ? (любой один символ) 
path - строка, в которой содержится комбинация пути и маски.

fileinfo - указатель на объект структуры , в который запишется информация о найденном файле. Структура _finddata_t содержит следующие поля:

unsigned attrib - содержит информацию об атрибутах файла. 
_A_NORMAL - Обычный файл без запретов на чтение или запись. 
_A_RDONLY - Файл только для чтения. 
_A_HIDDEN - Скрытый файл. 
_A_SYSTEM - Системный файл. 
_A_SUBDIR - Директория. 
_A_ARCH - Архивный файл. 
time_t time_create - время/дата создания файла (равно -1 для FAT систем). 
time_t time_access - время/дата последнего открытия файла (равно -1 для FAT систем). 
time_t time_write - время/дата последнего редактирования файла 
_fsize_t size - размер файла 
char name[260] - имя файла

Если всё пройдет успешно, информация о первом найденном файле запишется в объект структуры _finddata_t. При этом в оперативной памяти сформируется "список", обладающий внутренним указателем, который изначально будет установлен на первом найденном файле. В этом случае функция вернет уникальный номер, связанный с полученной группой файлов. Если поиск завершится неудачей, функция вернет -1.

  • _findnext(long done, _finddata_t * fileinfo) Функция осуществляет переход на следующий найденный файл в группе.

done - уникальный номер группы файлов в памяти. fileinfo - указатель на объект структуры, в который запишется информация о следующем найденном файле. Если достигнут конец списка файлов, функция вернет -1.

  • _findclose(long done) Функция очищает память от группы найденных файлов. done - уникальный номер группы файлов в памяти.
  • Если важна переносимость можно использовать класс boost/filesystem/operations.hpp
namespace boost
{
 namespace filesystem
 {
  bool remove( const path & ph );
  unsigned long remove_all( const path & ph );
 }
}

http://www.boost.org

  • BOOL DeleteFile(LPCTSTR lpFileName);
    CFile::Remove(LPCTSTR lpFileName);//Просто вызывает DeleteFile()
  • Мне остается только назвать неназванный способ:

WINSHELLAPI int WINAPI SHFileOperation(LPSHFILEOPSTRUCT lpFileOp)

Для этого надо заполнить основные поля структуры SHFILEOPSTRUCT:

HWND hwnd; — родительское окно

UINT wFunc; — в твоем случае FO_DELETE

LPCSTR pFrom; — путь удаляемго файла (можно использовать маски!)

FILEOP_FLAGS fFlags; — флаги; например OF_FILESONLY (мучить только файлы), OF_SILENT (не выводить индикатор) и др.

Там есть еще некоторые другие поля, разберешься. Подробнее читай Программист №1_2001 (http://www.programme.ru)

В чем преимущество этого способа: 1. Индикатор удаления. Если файлов много юзер сможет наблюдать виндосное окошко 2. Можно использовать маски в именах файлов

  • int _unlink( const char *filename );

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

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <io.h>
#include <ctime>

using namespace std;

int main(int argc, char** argv) {
		if(argc==1) exit(1);
		struct _finddata_t fileinfo;
		char str[200];
		strcpy(str,argv[1]);
		long done = _findfirst(str, &fileinfo);
		char szBuffer[30];

		//Преобразование времени
		ctime_s ( szBuffer, _countof (szBuffer),&fileinfo.time_write);
		
		printf ( " %-24s %.24s %9ld ", fileinfo.name, szBuffer, fileinfo.size);
		cout<<"\n";
		printf( ( fileinfo.attrib & _A_SUBDIR ) ? "d" : "-" );
		printf( ( fileinfo.attrib & _A_RDONLY ) ? "r" : "-" );
		printf( ( fileinfo.attrib & _A_HIDDEN ) ? "h" : "-" );
		printf( ( fileinfo.attrib & _A_SYSTEM ) ? "s" : "-" );
		printf( ( fileinfo.attrib & _A_ARCH )   ? "a" : "-" );
		printf( ( fileinfo.attrib & _A_NORMAL ) ? "n" : "-" );
		cout<<"\n";
		
		_findclose(done);
}
// Здесь находятся функции переименования и удаления
#include <stdio.h>
// Здесь находятся функции для поиска файлов    
#include <io.h>       
#include <string.h>
#include <iostream>
using namespace std;
// для функции AnsiToOem
#include <windows.h>

// Переименовать существующий файл
void RenameFile();

// Удалить существующий файл
void RemoveFile();   

// Поиск файлов в каталоге
void Dir();    

void main()
{
	// предлагаем выбрать пункт меню для выполнения
	cout << "Please, select preffer number...\n";

	//выбор пользователя
	char ch; 
	do{
		// Переименовать
		cout << "\n1 - Rename\n";
		// Удалить   
		cout << "2 - Remove\n";   
		// Просмотреть некоторую папку(каталог)
		cout << "3 - View some folder\n";	
		// Выход
		cout << "0 - Exit\n\n";    
		cin >> ch;

		// анализируем и вызываем 
		// соответствующую  функцию
		switch(ch)
		{
			case '1':
				RenameFile();   
			break;
			case '2':
				RemoveFile();   
			break;
			case '3':
				Dir();
			break;
		}			
	} while(ch != '0');      // Выход из программы
}

// Переименовать существующий файл
void RenameFile()       
{
	char oldName[50], newName[50];

	// В одной переменной запомним существующее имя (oldName), 
	cout << "Enter old name:";
	cin >> oldName;
		
	// А в другой новое имя(newName)
	cout << "Enter new name:";
	cin >> newName;

	// Произведем переименование и проверку результата
	if (rename(oldName, newName) != 0)
		cout << "Error!\n Couldn't rename file. Check old and new filename...\n\n";
	else 
		cout << "Ok...\n\n";
}

// Удалить существующий файл
void RemoveFile()           
{
	char Name[50];
	// Получаем имя и путь к удаляемому файлу
	cout << "Enter name:";
	cin >> Name;

	//Удаляем файл и проверяем результат	
	if (remove(Name) != 0)
		cout << "Error!\n Couldn't remove file. Check filename...\n";
	else 
		cout << "Ok...\n" ;
}

// Поиск файлов в каталоге
void Dir()                        
{
	// Запросим ПУТЬ (например, папка Temp на диске C, запишется 
	// таким вот образом: c:\temp\)
	char path[70];
	cout << "\nEnter full path (for example, C:\\):";
	cin >> path;
		
	// Запросим маску файлов 
	char mask[15];
	cout << "\nEnter mask (for example, *.* or *.txt):";
	cin >> mask;

	// Соединив две строки, мы получим результат
	// т.е. что хочет найти пользователь и где
	strcat(path, mask);
		
	// Объявление указателя fileinfo на структуру _finddata_t
	// и создание динамического объекта структуры _finddata_t
    _finddata_t *fileinfo=new _finddata_t;	

	// Начинаем поиск
	long done = _findfirst(path,fileinfo);

	// если done будет равняться -1, 
	// то поиск вести бесмысленно
	int MayWeWork = done;	

	// Счетчик, содержит информацию о количестве найденых файлов.	
	int count = 0;   
	while (MayWeWork!=-1)
	{	
		count++;
		// перекодировали имя найденного файла
		// на случай, если оно кириллическое
		AnsiToOem(fileinfo->name,fileinfo->name);
		// Распечатали имя найденного файла
		cout << fileinfo->name << "\n\n"; 
		// Пытаемся найти следующий файл из группы
		MayWeWork = _findnext(done, fileinfo);        
	}
	// Вывод сообщения о количестве найденных файлов.
	cout << "\nInformation: was found " << count;
	cout << " file(s) in folder..." << path << "\n\n";

	// Очистка памяти
	_findclose(done);
	delete fileinfo;
}

Операции по работе с директориями

Библиотека direct.h

  • int _mkdir( const char *dirname )

Создает директорию по указанному пути.

dirname - Путь и имя для создаваемой директории. В одно и то же время может быть создан только одна директория.

  • int _rmdir( const char *dirname )

Удаляет директорию по указанному пути.

dirname - Путь и имя для удаляемой директории.

Обе функции возвращают -1 в случае ошибки.

Для переименования директории можно использовать функцию rename из библиотеки stdio.h.
Удалить и переименовать можно только пустую директорию!!!

Пример на работу с директориями.

#include <iostream>
#include <direct.h>
#include <stdio.h>

using namespace std;

// Переименовать существующую директорию
void RenameDirectory();

// Удалить существующую директорию
void RemoveDirectory();   

// создать директорию
void CreateDirectory();    

void main()
{
	// предлагаем выбрать пункт меню для выполнения
	cout << "Please, select preffer number...\n";

	//выбор пользователя
	char ch; 
	do{
		// Переименовать
		cout << "\n1 - Rename\n";
		// Удалить   
		cout << "2 - Remove\n";   
		// Создать
		cout << "3 - Create\n";	
		// Выход
		cout << "0 - Exit\n\n";    
		cin >> ch;

		// анализируем и вызываем 
		// соответствующую  функцию
		switch(ch)
		{
			case '1':
				RenameDirectory();   
			break;
			case '2':
				RemoveDirectory();   
			break;
			case '3':
				CreateDirectory();
			break;
		}			
	} while(ch != '0');      // Выход из программы
}

// Переименовать существующую директорию
void RenameDirectory()       
{
	char oldName[50], newName[50];

	// В одной переменной запомним существующее имя (oldName), 
	cout << "Enter old name:";
	cin >> oldName;
		
	// А в другой новое имя(newName)
	cout << "Enter new name:";
	cin >> newName;

	// Произведем переименование и проверку результата
	if (rename(oldName, newName) != 0)
		cout << "Error!\n Couldn't rename directory.\n\n";
	else 
		cout << "Ok...\n\n";
}

// Удалить существующую директорию
void RemoveDirectory()           
{
	char Name[50];
	// Получаем имя и путь к удаляемой директории
	cout << "Enter name:";
	cin >> Name;

	//Удаляем директорию и проверяем результат	
	if (_rmdir(Name) == -1)
		cout << "Error!\n Couldn't remove directory.\n";
	else 
		cout << "Ok...\n" ;
}
// Создать директорию
void CreateDirectory()           
{
	char Name[50];
	// Получаем имя и путь к создаваемой директории
	cout << "Enter name:";
	cin >> Name;

	//Создаем директорию и проверяем результат	
	if (_mkdir(Name) == -1)
		cout << "Error!\n Couldn't create directory.\n";
	else 
		cout << "Ok...\n" ;
}

Практический пример. Показ содержимого директории.

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

cd Путь - переход в другую директорию.

cd .. - показ содержимого родительского каталога и переход. 
cd или cd . - показ содержимого текущего каталога. 
exit - выход из программы. 
root - переход в корневой каталог.
Все вышеперечисленные команды регистронезависимы.
#include <iostream>
#include <windows.h>
#include <io.h>
#include <stdio.h>
using namespace std;

const int size=255;

// Функция, которая убирает лишние слеши и пробелы справа 
void RemoveRSpacesAndRSlashes(char *str){
	int index=strlen(str)-1;
	while(str[index]=='\\'||str[index]==' '){
		index--;
	}
	strncpy(str,str,index);
	str[index+1]='\0';
}
// Функция для показа текущей директории
void ShowCurrentDir(char path[],char temp[]){
	CharToOem(path,temp);
	printf("%s>",temp);
}
// Функция перевода из кодировки
// Windows в кодировку DOS
// Для корректного отображения
// кирилицы
void RussianMessage(char path[]){
	CharToOem(path,path);
}

// Показ на экран содержимого папки
bool ShowDir(char path[]){
	// Показ содержимого текущей директории
	_finddata_t find;
	char pathfind[MAX_PATH];
	strcpy(pathfind,path);
	strcat(pathfind,"\\*.*");
	char info[MAX_PATH];
	
	// Начало Поиска
	int result=_findfirst(pathfind,&find);
	// Очистка экрана
	system("cls");
	int flag=result;
	if (flag==-1) {
		strcpy(info,"Такой Директории Нет");
		RussianMessage(info);
		printf("%s\n",info);
		return false;
	}
	
	while(flag!=-1){
			
		if(strcmp(find.name,".")&&strcmp(find.name,"..")){
			// Проверяем Директория или Нет
			find.attrib&_A_SUBDIR?strcpy(info," Каталог "):strcpy(info," Файл ");
			RussianMessage(info);
			RussianMessage(find.name);
			printf("%30s %10s\n",find.name,info);
		}
		// Продолжаем Поиск
		flag=_findnext(result,&find);
	}

	ShowCurrentDir(path,info);
	// Очищаем ресурсы, выделенные под поиск
	_findclose(result);
	return true;
}

void main(){
	// В данной переменной будет храниться путь к Директории
	char path[MAX_PATH];
	// В данной переменной будет команда, введенная пользователем
	char action[size];
	// Временная переменная
	char temp[MAX_PATH];
	// Получаем Путь к текущей Директории
	GetCurrentDirectory(sizeof(path),path);

	bool flag=true;

	// Показ содержимого текущей директории
	ShowDir(path);
	do{
		// Ввод команды пользователя
		cin.getline(action,size);
		// Убираем пробелы и слэши справа
		RemoveRSpacesAndRSlashes(action);
		// Переход в корневой каталог
		if(!strcmpi(action,"root")){
			path[2]='\0';
			ShowDir(path);	
		}
		// Проверка на желание пользователя выйти 
		else if(!strcmpi(action,"exit")){
			flag=false;
		}
		// Проверка на команду cd
		else if(!strnicmp(action,"cd",2)){
			// Показ содержимого текущей директории
			if((!strcmpi(action,"cd"))){
				// Показ Директории
				ShowDir(path);
			}
			// Команда cd была дана с параметрами 
			else if(!strnicmp(action,"cd ",3)){
				// Находим индекс параметра 
				int index=strspn(action+2," ");
				if(index){
					// Проверка на полный путь к Директории
					if(strchr(action+index+2,':')){
						// Попытка отобразить содержимое Директории	
						if(ShowDir(action+index+2)){
							strcpy(path,action+index+2);
						}
						else{
							// Произошла Ошибка 	
							ShowCurrentDir(path,temp);
						}
					}
					// Поднимаемся в родительский каталог
					else if(!strcmp(action+index+2,"..")){
						char *result=strrchr(path,'\\');	
						if(result){
							int delta=result-path;	
							strncpy(temp,path,delta);
							temp[delta]='\0';
						}
						else{
							strcpy(temp,path);
						}
							
						if(ShowDir(temp)){
							strcpy(path,temp);
						}
						else{
							// Произошла Ошибка 	
							ShowCurrentDir(path,temp);
						}
					}
					// Показ Директории
					else if(!strcmp(action+index+2,".")){
						ShowDir(path);
					}
					else if(!strcmp(action+index+2,"/")){
						ShowDir(path);
					}
					else{
						// Был Дан неполный путь 
						strcpy(temp,path);
						strcat(temp,"\\");
		 				strcat(temp,action+index+2);
						// Попытка отобразить содержимое Директории	
						if(ShowDir(temp)){
							strcpy(path,temp);
						}
						else{
							// Произошла Ошибка 	
							ShowCurrentDir(path,temp);
						}
					}
				}
				else{
					// Показ Директории
					ShowDir(path);
				}
			}
			else{
				// Показ Директории
				ShowDir(path);
			}
		}
		else{
			// Показ Директории
			ShowDir(path);
		}
	}while(flag);

}
PQ VPS сервера в 28+ странах.