Сайт о телевидении

Сайт о телевидении

» » Последовательность работы с файлом c. Работа с файлами в языке си

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

– сравнение для выявления равенства либо неравенства.

Практическое назначение перечисления – определение множества различающихся символических констант целого типа.

Пример использования переменных перечислимого типа:

mo=1, tu, we, th, fr, sa, su } days;

puts(“ Введите день недели (от 1 до 7) : ”); scanf(“%d”, &t_day);

w_day = su; start = mo;

end = w_day -t_day;

printf(“\n Понедельник - %d-й день недели, \ сейчас %d-й день. \n\

До конца недели %d дней (дня). ”, start, t_day, end);

Результат работы программы: Введите день недели (от 1 до 7) : 2

Понедельник - 1-й день недели, сейчас 2-й день. До конца недели 5 дней (дня).

18. Файлы в языке Си

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

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

Бинарные (двоичные) файлы представляют собой последовательность данных, структура которых определяется программно.

В языке Си имеется большой набор функций для работы с файлами, большинство которых находятся в библиотеках stdio.h иio.h .

18.1. Открытие файла

Каждому файлу присваивается внутреннее логическое имя, используемое в дальнейшем при обращении к нему. Логическое имя (идентификатор файла) – это

указатель на файл, т.е. на область памяти, где содержится вся необходимая информация о файле. Формат объявления указателя на файл следующий:

FILE * указатель на файл;

FILE – идентификатор структурного типа, описанный в стандартной библиотеке

stdio.h и содержащий следующую информацию:

type struct {

– число оставшихся в буфере непрочитанных байт;

обычный размер буфера – 512 байт; как только level=0,

в буфер из файла читается следующий блок данных;

– флаг статуса файла – чтение, запись, дополнение;

– дескриптор файла, т.е. число, определяющее его но-

unsigned char hold;

– непереданный символ, т.е. ungetc-символ;

– размер внутреннего промежуточного буфера;

unsigned char buffer;

– значение указателя для доступа внутри буфера, т.е.

задает начало буфера, начало строки или текущее зна-

чение указателя внутри буфера в зависимости от режи-

ма буферизации;

unsigned char *curp;

– текущее значение указателя для доступа внутри бу-

фера, т.е. задает текущую позицию в буфере для обме-

на с программой;

unsigned istemp;

– флаг временного файла;

– флаг при работе с файлом;

} FILE;

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

FILE* fopen (char* имя_ файла, char* режим);

она берет внешнее представление – физическое имя файла на носителе (дискета, винчестер) и ставит ему в соответствие логическое имя.

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

– строкой, например, “a:Mas_dat.dat” – файл с именем Mas_dat.dat, находящийся на дискете, “d:\\work\\Sved.txt” – файл с именем Sved.txt, находящийся на винчестере в каталоге work.

Внимание! Обратный слеш (\), как специальный символ, в строке записывается дважды.

При успешном открытии функция fopen возвращает указатель на файл (в дальнейшем – указатель файла). При ошибке возвращаетсяNULL . Данная ситуация обычно возникает, когда неверно указывается путь к открываемому файлу. Например, если в дисплейном классе нашего университета указать путь, запрещенный для записи (обычно разрешенным является d:\work\).

Второй параметр – строка, в которой задается режим доступа к файлу:

w – файл открывается для записи; если файла с заданным именем нет, то он будет создан; если такой файл существует, то перед открытием прежняя информация уничтожается;

r – файл открывается только для чтения; если такого файла нет, то возникает ошибка;

a – файл открывается для добавления в конец новой информации;

r+ – файл открывается для редактирования данных – возможны и запись, и чтение информации;

w+ – то же, что и для r+;

a+ – то же, что и для a, только запись можно выполнять в любое место файла; доступно и чтение файла;

t – файл открывается в текстовом режиме;b – файл открывается в двоичном режиме.

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

По умолчанию файл открывается в текстовом режиме. Пример: FILE *f; – объявляется указатель на файл f;

f = fopen ("d:\\work\\Dat_sp.cpp", "w"); – открывается для записи файл с логическим именем f, имеющим физическое имя Dat_sp.cpp, находящийся на диске d, в каталоге work; или более кратко

FILE *f = fopen ("d:\\work\\Dat_sp.cpp", "w");

18.2. Закрытие файла

После работы с файлом доступ к нему необходимо закрыть. Это выполняет функция int fclose (указатель файла ). Например, из предыдущего примера файл закрывается так: fclose (f);

Для закрытия нескольких файлов введена функция, объявленная следующим образом: void fcloseall (void );

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

FILE* freopen (char*имя_файла , char *режим , FILE *указатель_файла );

Эта функция сначала закрывает файл, объявленный указателем_файла (как это делает функцияfopen ), а затем открывает файл сименем_файла и правами доступа «режим ».

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

FILE* tmpfile (void);

которая создает на диске временный файл с правами доступа «w+b», после завершения работы программы или после закрытия временного файла он автоматически удаляется.

18.3. Запись – чтение информации

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

Рассмотрим основные функции, применяемые в каждой из указанных трех групп операций.

Посимвольный ввод-вывод

В функциях посимвольного ввода-вывода происходит прием одного символа из файла или передача одного символа в файл:

Построчный ввод-вывод

В функциях построчного ввода-вывода происходит перенос из файла или в

Блоковый ввод-вывод

В функциях блокового ввода-вывода работа происходит с целыми блоками

информации:

int fread (void*p, intsize,

– считывает n блоков поsize байт каждый из фай-

int n, FILE *f)

ла f в область памяти с указателемp (необходимо

int fwrite (void*p, intsize,

заранее отвести память под считываемый блок);

– записывает n блоков по size байт каждый из об-

int n, FILE *f)

ласти памяти с указателем p в файл f.

Форматированный ввод-вывод производится функциями.

Функция fopen() открывает для использования поток, связывает файл с данным потоком и затем возвращает указатель FILE на данный поток. Чаще всего файл рас­сматривается как дисковый файл. Функция fopen() имеет следующий прототип:

FILE *fopen(const char *имя_файла, const char *режим);

Где режим указывает на строку, содержащую желаемый режим открытия файла. Допустимые зна­чения для режим в Borland С++ показаны в таблице. имя_файла должно быть строкой симво­лов, предоставляющей корректное имя файла операционной системе, и может содержать указа­ние пути.

Функция fopen() возвращает указатель базового типа FILE. Данный указатель идентифицирует файл и используется большинством функций файловой системы. Его никогда не следует изменять самостоятельно. Функция возвращает нулевой указатель, если файл не может быть открыт.

Как показывает таблица, файл может быть открыт или в текстовом, или в двоичном режи­ме. В текстовом режиме при вводе последовательность возврат каретки и перевод строки трансли­руется в символ новой строки. При выводе справедливо обратное: символ новой строки трансли­руется в возврат каретки и перевод строки. В двоичных файлах такого перевода не происходит. Когда в аргументе режима не указаны ни t, ни b, то статус файла текстовый/двоичный определя­ется значением глобальной переменной _fmode, определенной в Borland С++. По умолчанию fmode установлена в О_ТЕХТ, то есть устанавливается текстовый режим. Если установить _fmode в О_BINARY, то файлы будут открываться в двоичном режиме. (Эти макросы определены в fcntl.h.) Естественно, использование явно указанных t или b устраняет эффекты, связанные с переменной _fmode. Кроме этого, _fmode характерна только для продуктов Borland. Она не определена в системе ввода/вывода ANSI С.

Если необходимо открыть файл с именем test на запись, то следует написать:

Fp = fopen ("test", "w") ;

Где fp - это переменная типа FILE *. Тем не менее обычно можно увидеть следующее:

If((fp = fopen("test", "w"))==NULL) {
puts ("Cannot open file.");
exit (1);
}

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

Если fopen() используется для открытия файла на запись, то любой ранее существующий файл с указанным именем будет удален. Если файла с указанным именем не существует, то он будет создан.

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

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

Таблица: Допустимые значения режимов

Значение

Открывает файл для чтения. (Открывает по умолчанию как текстовый файл.)

Создает файл для записи. (Открывает по умолчанию как текстовый файл.)

Присоединяет к файлу. (Открывает по умолчанию как текстовый файл.)

Открывает двоичный файл для чтения.

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

Присоединяет к двоичному файлу.

Открывает файл для чтения/записи. (Открывает по умолчанию как текстовый файл.)

Создает файл для чтения/записи. (Открывает по умолчанию как текстовый файл.)

Присоединяет или создает файл для чтения/записи. (Открывает по умолчанию как текстовый файл.)

Открывает двоичный файл для чтения/записи.

Создает двоичный файл для чтения/записи.

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

Создает текстовый файл для записи.

Присоединяет к текстовому файлу.

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

Создает текстовый файл для чтения/записи.

Открывает или создает текстовый файл для чтения/записи.

Работа с текстовыми файлами в C++.

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

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

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

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

В программах на C++ при работе с текстовыми файлами необходимо подключать библиотеки iostream и fstream.

Для того чтобы записывать данные в текстовый файл, необходимо:

    описать переменную типа ofstream.

    вывести информацию в файл.

    обязательно закрыть файл.

Для считывания данных из текстового файла, необходимо:

    описать переменную типа ifstream.

    открыть файл с помощью функции open.

    закрыть файл.

Запись информации в текстовый файл

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

    Будет создана переменная F для записи информации в файл.

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

F.open(«file», mode);

Здесь F - переменная, описанная как ofstream,

file - полное имя файла на диске,

mode - режим работы с открываемым файлом.

Обратите внимание на то, что при указании полного имени файла нужно ставить двойной слеш. Например, полное имя файла noobs.txt, находящегося в папке game на диске D:, нужно будет записать так:

D:\\game\\noobs.txt.

Файл может быть открыт в одном из следующих режимов:

ios::in - открыть файл в режиме чтения данных, этот режим является режимом по умолчанию для потоков ifstream;

ios::out - открыть файл в режиме записи данных (при этом информация о существующем файле уничтожается), этот режим является режимом по умолчанию для потоков ofstream;

ios::app - открыть файл в режиме записи данных в конец файла;

ios::ate - передвинуться в конец уже открытого файла;

ios::trunc - очистить файл, это же происходит в режиме ios::out;

ios::nocreate - не выполнять операцию открытия файла, если он не существует;

ios::noreplace - не открывать существующий файл.

Параметр mode может отсутствовать, в этом случае файл открывается в режиме по умолчанию для данного потока.

После удачного открытия файла (в любом режиме) в переменной F будет храниться true, в противном случае false. Это позволит проверить корректность операции открытия файла.

Открыть файл (в качестве примера возьмем файл D:\\game\\noobs.txt) в режиме записи можно одним из следующих способов:

// первый способ

ofstream F;

F.open("D:\\game\\noobs.txt", ios::out);

//второй способ, режим ios::out является режимом по умолчанию

// для потока ofstream

ofstream F;

//третий способ объединяет описание переменной и типа поток

//и открытие файла в одном операторе

ofstream F ("D:\\game\\noobs.txt", ios::out);

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

Если вы хотите открыть существующий файл в режиме до записи, то в качестве режима следует использовать значение ios::app.

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

Например, для записи в поток F переменной a, оператор вывода будет иметь вид:

Для последовательного вывода в поток G переменных b, c, d оператор вывода станет таким:

G<

Закрытие потока осуществляется с помощью оператора:

ПРИМЕР:

Создать текстовый файл D:\\game\\noobs.txt и записать в него n вещественных чисел.

#include "stdafx.h"

#include

#include

#include

using namespace std;

int main()

setlocale (LC_ALL, "RUS");

int i, n;

double a;

//описывает поток для записи данных в файл

ofstream f ;

//открываем файл в режиме записи,

//режим ios :: out устанавливается по умолчанию

f.open("D:\\game\\noobs.txt", ios::out);

//вводим количество вещественных чисел

cout <<" n ="; cin >> n ;

//цикл для ввода вещественных чисел

//и записи их в файл

for (i=0; i

cout<<"a=";

//ввод числа

cin>>a;

f<

//закрытие потока

f.close();

system("pause");

return 0;

_______________________________________________________________

Для того чтобы прочитать информацию из текстового файла, необходимо описать переменную типа ifstream . После этого нужно открыть файл для чтения с помощью оператора open . Если переменную назвать F, то первые два оператора будут такими:

F.open("D:\\game\\noobs.txt", ios::in);

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

Например, для чтения из потока F в переменную a, оператор ввода будет выглядеть так:

Два числа в текстовом редакторе считаются разделенными, если между ними есть хотя бы один из символов: пробел, табуляция, символ конца строки. Хорошо, если программисту заранее известно, сколько и каких значений храниться в текстовом файле. Однако часто просто известен тип значений, хранящихся в файле, при этом их количество может быть различным. При решении подобной проблемы необходимо считывать значения из файла по одному, а перед каждым считыванием проверять, достигнут ли конец файла. Для этого существует функция F . eof ().

Здесь F - имя потока функция возвращает логическое значение: true или false, в зависимости от того достигнут ли конец файла. Следовательно, цикл для чтения содержимого всего файла можно записать так:

//организуем для чтения значений из файла, выполнение

//цикла прервется, когда достигнем конец файла,

//в этом случае F.eof() вернет истину

while (!F.eof())

ПРИМЕР:

В текстовом файле D:\\game\\noobs.txt хранятся вещественные числа, вывести их на экран и вычислить их количество.

#include "stdafx.h"

#include

#include

#include

#include

using namespace std;

int main()

setlocale (LC_ALL, "RUS");

int n=0;

float a;

fstream F;

//открываем файл в режиме чтения

F.open("D:\\game\\noobs.txt");

//если открытие файла прошло корректно, то

//цикл для чтения значений из файла; выполнение цикла прервется,

//когда достигнем конца файла, в этом случае F.eof() вернет истину.

while (!F.eof())

//чтение очередного значения из потока F в переменную a

F>>a;

//вывод значения переменной a на экран

cout<

//увеличение количества считанных чисел

//закрытие потока

F.close();

//вовод на экран количества считанных чисел

cout<<"n="<

//если открытие файла прошло некорректно, то вывод

//сообщения об отсутствии такого файла

else cout<<" Файл не существует"<

system("pause");

return 0;

C++. Обработка двоичных файлов

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

Для того чтобы записать данные в двоичный файл, необходимо:

    описать файловую переменную типа FAIL * с помощью оператора FILE *filename;. Здесь filename - имя переменной, где будет храниться указатель на файл.

    записать информацию в файл с помощью функции fwrite

Для того чтобы считат ь данные из двоичного файла, необходимо:

    описать переменную типа FILE *

    открыть файл с помощью функции fopen

    закрыть файл с помощью функции fclose

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

Для открытия файла предназначена функция fopen.

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

Здесь filename - строка, в которой хранится полное имя открываемого файла, mode - строка, определяющая режим работы с файлом; возможны следующие значения:

«rb» - открываем двоичный файл в режиме чтения;

«wb» - создаем двоичный файл для записи; если он существует, то его содержимое очищается;

«ab» - создаем или открываем двоичный файл для дозаписи в конец файла;

«rb+» - открываем существующий двоичный файл в режиме чтения и записи;

«wb+» - открываем двоичный файл в режиме чтения и записи, существующий файл очищается;

«ab+» - двоичный файл открывается или создается для исправления существующий информации и добавления новой в конец файла.

Функция возвращает в файловой переменной f значение NULL в случае неудачного открытия файла. После открытия файла доступен 0-й его байт, указатель файла равен 0, значение которого по мере чтения или записи смещается на считанное (записанное) количество байт. Текущие значение указателя файла - номер байта, начиная с которого будет происходить операция чтения или записи.

Для закрытия файла предназначена функция fclose

int fclose(FILE *filename);

Возвращает 0 при успешном закрытие файла и NULL в противном случае.

Функция remove предназначена для удаления файлов.

int remove(const char *filename);

Эта функция удаляет с диска файл с именем filenema. Удаляемый файл должен быть закрыт. Функция возвращает ненулевое значение, если файл не удалось удалить.

Для переименования файлов предназначена функция rename:

int rename(const char *oldfilename, const char *newfilename);

Первый параметр - старое имя файла, второй - новое. Возвращает 0 при удачном завершении программы.

Чтение из двоичного файла осуществляется с помощью функции fread:

fread(void *ptr, size, n, FILE *filename);

Функция fread считывает из файла filename в массив ptr n элементов размера size. Функция возвращает количество считанных элементов. После чтения из файла его указатель смещается на n*size байт.

Запись в двоичный файл осуществляется с помощью функции fwrite:

fwrite(const void *ptr, size, n, FILE *filename);

Функция fwrite записывает в файл filename из массива ptr n элементов размера size. Функция возвращает количество записанных элементов. После записи информации в файл указатель смещается на n*size байт.

Для контроля достижения конца файла есть функция feof:

int feof(FILE *filename);

Она возвращает ненулевое значение если достигнут конец файла.

ПРИМЕР:

Создать двоичный файл D:\\game\\noobs.dat и записать в него целое число n и n вещественных чисел.

#include "stdafx.h"

#include

using namespace std;

int main()

setlocale (LC_ALL, "RUS");

int n, i;

double a;

//создаем двоичный файл в режиме записи

f=fopen("D:\\game\\noobs.dat", "wb");

// ввод числа n

cout<<"n="; cin>>n;

fwrite(&n, sizeof(int), 1, f);

//цикл для ввода n вещественных чисел

for (i=0; i

//ввод очередного вещественного числа

cout<<"a=";

cin>>a;

//запись вешественного числа в двоичный файл

fwrite(&a, sizeof(double), 1, f);

// закрываем файл

fclose(f);

system("pause");

return 0;

ПРИМЕР:

Вывести на экран содержимого созданного в прошлой задаче двоичного файла D:\\game\\noobs.dat

#include "stdafx.h"

#include

using namespace std;

int main()

setlocale (LC_ALL, "RUS");

int n, i;

double *a;

FILE *f; //описываем файловую переменную

//открываем существующий двоичный файл в режиме чтения

//считываем из файла одно целое число в переменную n

//вывод n на экран

cout<<"n="<

//выделение памяти для массива из n чисел

a=new double[n];

//чтение n вещественных чисел из файла в массив a

//вывод массива на экран

for (i=0; i

cout<

cout<

// закрываем файл

fclose(f);

system("pause");

return 0;

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

int n, i;

double a;

FILE *f;

f=fopen("D:\\game\\noobs.dat", "rb");

for (i=0; i<15; i++)

fclose(f);

f=fopen("D:\\game\\noobs.dat", "rb");

fread(&a, sizeof(double), 1, f);

fclose(f);

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

int fseek(FILE *filename, long int offset, int origin);

Функция устанавливает указатель текущий позиции файла F в соответствии со значением начала отсчета origin и смещения offset. Параметр offset равен количеству байтов, на которые будет смещен указатель файла относительно начала отсчета, заданного параметром origin. В качестве значения для параметра origin должно быть взято одно из следующих значений отсчета смещения offset, определенных в заголовке stdio.h:

SEEK_SET - с начала файла;

SEEK_CUR - с текущей позиции;

SEEK_END - с конца файла.

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

Функция fseek фактически реализует прямой доступ к любому значению в файле. Необходимо только знать месторасположение (номер байта) значения в файле. Рассмотрим использование прямого доступа в двоичных файлах на примере решения следующей задачи.

ПРИМЕР

В созданном раннее двоичном файле D:\\game\\noobs.dat, поменять местами наибольшее и наименьшее из вещественных чисел.

Алгоритм решения задачи состоит из следующих этапов:

    чтение вещественных из файла в массив a.

    поиск в массиве а максимального (max) и минимального (min) значения и их номеров (imax, imin).

    перемещения указателя файла к максимальному значению и запись min.

    перемещения указателя файла к минимальному значению и запись max.

Ниже приведен текст программы решения задачи с комментариями.

#include "stdafx.h"

#include

using namespace std;

int main()

setlocale (LC_ALL, "RUS");

int n, i, imax, imin;

double *a, max, min;

FILE *f;

//открытие файла в режиме чтения и записи

f=fopen("D:\\game\\noobs.dat", "rb+");

//считываем из файла в переменную n количество

//вещественных чисел в файле

fread(&n, sizeof(int), 1, f);

cout<<"n="<

//выделяем память для хранения вещественных чисел,

//которые будут храниться в массиве a

a=new double[n];

//считываем из файла в массив а вещественные числа

fread(a, sizeof(double), n, f);

//поиск максимального и минимального элементов

//в массиве а и их индексов

for (imax=imin=0, max=min=a, i=1; i

if (a[i]>max)

max=a[i];

if (a[i]

min=a[i];

// перемещение указателя к максимальному элементу

fseek(f, sizeof(int)+imax*sizeof(double), SEEK_SET);

//запись min вместо максимального элемента файла

fwrite(&min, sizeof(double), 1, f);

// перемещение указателя к минимальному элементу

fseek(f, sizeof(int)+imin*sizeof(double), SEEK_SET);

//запись max вместо минимального элемента файла

fwrite(&max, sizeof(double), 1, f);

//закрытие файла

fclose(f);

//освобождение памяти

delete [ ]a;

system("pause");

Последнее обновление: 31.10.2015

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

Рассмотрим наиболее важные его свойства и методы:

    Свойство Length : возвращает длину потока в байтах

    Свойство Position : возвращает текущую позицию в потоке

    Метод Read : считывает данные из файла в массив байтов. Принимает три параметра: int Read(byte array, int offset, int count) и возвращает количество успешно считанных байтов. Здесь используются следующие параметры:

    • array - массив байтов, куда будут помещены считываемые из файла данные

      offset представляет смещение в байтах в массиве array, в который считанные байты будут помещены

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

    Метод long Seek(long offset, SeekOrigin origin) : устанавливает позицию в потоке со смещением на количество байт, указанных в параметре offset.

    Метод Write : записывает в файл данные из массива байтов. Принимает три параметра: Write(byte array, int offset, int count)

    • array - массив байтов, откуда данные будут записываться в файла

      offset - смещение в байтах в массиве array, откуда начинается запись байтов в поток

      count - максимальное число байтов, предназначенных для записи

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

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

Посмотрим на примере считывания-записи в текстовый файл:

Console.WriteLine("Введите строку для записи в файл:"); string text = Console.ReadLine(); // запись в файл using (FileStream fstream = new FileStream(@"C:\SomeDir\noname\note.txt", FileMode.OpenOrCreate)) { // преобразуем строку в байты byte array = System.Text.Encoding.Default.GetBytes(text); // запись массива байтов в файл fstream.Write(array, 0, array.Length); Console.WriteLine("Текст записан в файл"); } // чтение из файла using (FileStream fstream = File.OpenRead(@"C:\SomeDir\noname\note.txt")) { // преобразуем строку в байты byte array = new byte; // считываем данные fstream.Read(array, 0, array.Length); // декодируем байты в строку string textFromFile = System.Text.Encoding.Default.GetString(array); Console.WriteLine("Текст из файла: {0}", textFromFile); } Console.ReadLine();

Разберем этот пример. И при чтении, и при записи используется оператор using . Не надо путать данный оператор с директивой using, которая подключает пространства имен в начале файла кода. Оператор using позволяет создавать объект в блоке кода, по завершению которого вызывается метод Dispose у этого объекта, и, таким образом, объект уничтожается. В данном случае в качестве такого объекта служит переменная fstream .

Объект fstream создается двумя разными способами: через конструктор и через один из статических методов класса File.

Здесь в конструктор передается два параметра: путь к файлу и перечисление FileMode . Данное перечисление указывает на режим доступа к файлу и может принимать следующие значения:

    Append : если файл существует, то текст добавляется в конец файл. Если файла нет, то он создается. Файл открывается только для записи.

    Create : создается новый файл. Если такой файл уже существует, то он перезаписывается

    CreateNew : создается новый файл. Если такой файл уже существует, то он приложение выбрасывает ошибку

    Open : открывает файл. Если файл не существует, выбрасывается исключение

    OpenOrCreate : если файл существует, он открывается, если нет - создается новый

    Truncate : если файл существует, то он перезаписывается. Файл открывается только для записи.

Статический метод OpenRead класса File открывает файл для чтения и возвращает объект FileStream.

Конструктор класса FileStream также имеет ряд перегруженных версий, позволяющий более точно настроить создаваемый объект. Все эти версии можно посмотреть на msdn.

И при записи, и при чтении применяется объект кодировки Encoding.Default из пространства имен System.Text . В данном случае мы используем два его метода: GetBytes для получения массива байтов из строки и GetString для получения строки из массива байтов.

В итоге введенная нами строка записывается в файл note.txt . По сути это бинарный файл (не текстовый), хотя если мы в него запишем только строку, то сможем посмотреть в удобочитаемом виде этот файл, открыв его в текстовом редакторе. Однако если мы в него запишем случайные байты, например:

Fstream.WriteByte(13); fstream.WriteByte(103);

То у нас могут возникнуть проблемы с его пониманием. Поэтому для работы непосредственно с текстовыми файлами предназначены отдельные классы - StreamReader и StreamWriter.

Произвольный доступ к файлам

Нередко бинарные файлы представляют определенную стрктуру. И, зная эту структуру, мы можем взять из файла нужную порцию информации или наоброт записать в определенном месте файла определенный набор байтов. Например, в wav-файлах непосредственно звуковые данные начинаются с 44 байта, а до 44 байта идут различные метаданные - количество каналов аудио, частота дискретизации и т.д.

С помощью метода Seek() мы можем управлять положением курсора потока, начиная с которого производится считывание или запись в файл. Этот метод принимает два параметра: offset (смещение) и позиция в файле. Позиция в файле описывается тремя значениями:

    SeekOrigin.Begin : начало файла

    SeekOrigin.End : конец файла

    SeekOrigin.Current : текущая позиция в файле

Курсор потока, с которого начинается чтение или запись, смещается вперед на значение offset относительно позиции, указанной в качестве второго параметра. Смещение может отрицательным, тогда курсор сдвигается назад, если положительное - то вперед.

Рассмотрим на примере:

Using System.IO; using System.Text; class Program { static void Main(string args) { string text = "hello world"; // запись в файл using (FileStream fstream = new FileStream(@"D:\note.dat", FileMode.OpenOrCreate)) { // преобразуем строку в байты byte input = Encoding.Default.GetBytes(text); // запись массива байтов в файл fstream.Write(input, 0, input.Length); Console.WriteLine("Текст записан в файл"); // перемещаем указатель в конец файла, до конца файла- пять байт fstream.Seek(-5, SeekOrigin.End); // минус 5 символов с конца потока // считываем четыре символов с текущей позиции byte output = new byte; fstream.Read(output, 0, output.Length); // декодируем байты в строку string textFromFile = Encoding.Default.GetString(output); Console.WriteLine("Текст из файла: {0}", textFromFile); // worl // заменим в файле слово world на слово house string replaceText = "house"; fstream.Seek(-5, SeekOrigin.End); // минус 5 символов с конца потока input = Encoding.Default.GetBytes(replaceText); fstream.Write(input, 0, input.Length); // считываем весь файл // возвращаем указатель в начало файла fstream.Seek(0, SeekOrigin.Begin); output = new byte; fstream.Read(output, 0, output.Length); // декодируем байты в строку textFromFile = Encoding.Default.GetString(output); Console.WriteLine("Текст из файла: {0}", textFromFile); // hello house } Console.Read(); } }

Консольный вывод:

Текст записан в файл Текст из файл: worl Текст из файла: hello house

Вызов fstream.Seek(-5, SeekOrigin.End) перемещает курсор потока в конец файлов назад на пять символов:

То есть после записи в новый файл строки "hello world" курсор будет стоять на позиции символа "w".

После этого считываем четыре байта начиная с символа "w". В данной кодировке 1 символ будет представлять 1 байт. Поэтому чтение 4 байтов будет эквивалентно чтению четырех сиволов: "worl".

Затем опять же перемещаемся в конец файла, не доходя до конца пять символов (то есть опять же с позиции символа "w"), и осуществляем запись строки "house". Таким образом, строка "house" заменяет строку "world".

Закрытие потока

В примерах выше дл закрытия потока применяется конструкция using . После того как все операторы и выражения в блоке using отработают, объект FileStream уничтожается. Однако мы можем выбрать и другой способ:

FileStream fstream = null; try { fstream = new FileStream(@"D:\note3.dat", FileMode.OpenOrCreate); // операции с потоком } catch(Exception ex) { } finally { if (fstream != null) fstream.Close(); }

Если мы не используем конструкцию using, то нам надо явным образом вызвать метод Close() : fstream.Close()

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

Файл – именованная область внешней памяти, выделенная для хранения массива данных. Данные, содержащиеся в файлах, имеют самый разнообразный характер: программы на алгоритмическом или машинном языке; исходные данные для работы программ или результаты выполнения программ; произвольные тексты; графические изображения и т. п.

Каталог (папка , директория ) – именованная совокупность байтов на носителе информации, содержащая название подкаталогов и файлов, используется в файловой системе для упрощения организации файлов.

Файловой системой называется функциональная часть операционной системы, обеспечивающая выполнение операций над файлами. Примерами файловых систем являются FAT (FAT – File Allocation Table, таблица размещения файлов), NTFS, UDF (используется на компакт-дисках).

Существуют три основные версии FAT: FAT12, FAT16 и FAT32. Они отличаются разрядностью записей в дисковой структуре, т.е. количеством бит, отведённых для хранения номера кластера. FAT12 применяется в основном для дискет (до 4 кбайт), FAT16 – для дисков малого объёма, FAT32 – для FLASH-накопителей большой емкости (до 32 Гбайт).

Рассмотрим структуру файловой системы на примере FAT32.

Файловая структура FAT32

Устройства внешней памяти в системе FAT32 имеют не байтовую, а блочную адресацию. Запись информации в устройство внешней памяти осуществляется блоками или секторами.

Сектор – минимальная адресуемая единица хранения информации на внешних запоминающих устройствах. Как правило, размер сектора фиксирован и составляет 512 байт. Для увеличения адресного пространства устройств внешней памяти сектора объединяют в группы, называемые кластерами.

Кластер – объединение нескольких секторов, которое может рассматриваться как самостоятельная единица, обладающая определёнными свойствами. Основным свойством кластера является его размер, измеряемый в количестве секторов или количестве байт.

Файловая система FAT32 имеет следующую структуру.

Нумерация кластеров, используемых для записи файлов, ведется с 2. Как правило, кластер №2 используется корневым каталогом, а начиная с кластера №3 хранится массив данных. Сектора, используемые для хранения информации, представленной выше корневого каталога, в кластеры не объединяются.
Минимальный размер файла, занимаемый на диске, соответствует 1 кластеру.

Загрузочный сектор начинается следующей информацией:

  • EB 58 90 – безусловный переход и сигнатура;
  • 4D 53 44 4F 53 35 2E 30 MSDOS5.0;
  • 00 02 – количество байт в секторе (обычно 512);
  • 1 байт – количество секторов в кластере;
  • 2 байта – количество резервных секторов.

Кроме того, загрузочный сектор содержит следующую важную информацию:

  • 0x10 (1 байт) – количество таблиц FAT (обычно 2);
  • 0x20 (4 байта) – количество секторов на диске;
  • 0x2С (4 байта) – номер кластера корневого каталога;
  • 0x47 (11 байт) – метка тома;
  • 0x1FE (2 байта) – сигнатура загрузочного сектора (55 AA ).

Сектор информации файловой системы содержит:

  • 0x00 (4 байта) – сигнатура (52 52 61 41 );
  • 0x1E4 (4 байта) – сигнатура (72 72 41 61 );
  • 0x1E8 (4 байта) – количество свободных кластеров, -1 если не известно;
  • 0x1EС (4 байта) – номер последнего записанного кластера;
  • 0x1FE (2 байта) – сигнатура (55 AA ).

Таблица FAT содержит информацию о состоянии каждого кластера на диске. Младшие 2 байт таблицы FAT хранят F8 FF FF 0F FF FF FF FF (что соответствует состоянию кластеров 0 и 1, физически отсутствующих). Далее состояние каждого кластера содержит номер кластера, в котором продолжается текущий файл или следующую информацию:

  • 00 00 00 00 – кластер свободен;
  • FF FF FF 0F – конец текущего файла.
  • 8 байт – имя файла;
  • 3 байта – расширение файла;

Корневой каталог содержит набор 32-битных записей информации о каждом файле, содержащих следующую информацию:

В случае работы с длинными именами файлов (включая русские имена) кодировка имени файла производится в системе кодировки UTF-16. При этого для кодирования каждого символа отводится 2 байта. При этом имя файла записывается в виде следующей структуры:

  • 1 байт последовательности;
  • 10 байт содержат младшие 5 символов имени файла;
  • 1 байт атрибут;
  • 1 байт резервный;
  • 1 байт – контрольная сумма имени DOS;
  • 12 байт содержат младшие 3 символа имени файла;
  • 2 байта – номер первого кластера;
  • остальные символы длинного имени.

Работа с файлами в языке Си

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

Когда поток открывается для ввода-вывода, он связывается со стандартной структурой типа FILE , которая определена в stdio.h . Структура FILE содержит необходимую информацию о файле.

Открытие файла осуществляется с помощью функции fopen() , которая возвращает указатель на структуру типа FILE , который можно использовать для последующих операций с файлом.

FILE *fopen(name, type);


name – имя открываемого файла (включая путь),
type — указатель на строку символов, определяющих способ доступа к файлу:
  • "r" - открыть файл для чтения (файл должен существовать);
  • "w" - открыть пустой файл для записи; если файл существует, то его содержимое теряется;
  • "a" - открыть файл для записи в конец (для добавления); файл создается, если он не существует;
  • "r+" - открыть файл для чтения и записи (файл должен существовать);
  • "w+" - открыть пустой файл для чтения и записи; если файл существует, то его содержимое теряется;
  • "a+" - открыть файл для чтения и дополнения, если файл не существует, то он создаётся.

Возвращаемое значение — указатель на открытый поток. Если обнаружена ошибка, то возвращается значение NULL .

Функция fclose() закрывает поток или потоки, связанные с открытыми при помощи функции fopen() файлами. Закрываемый поток определяется аргументом функции fclose() .

Возвращаемое значение: значение 0, если поток успешно закрыт; константа EOF , если произошла ошибка.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#include
int main() {
FILE *fp;
char name = "my.txt" ;
if ((fp = fopen(name, "r" )) == NULL )
{
printf("Не удалось открыть файл" );
getchar();
return 0;
}
// открыть файл удалось
... // требуемые действия над данными
fclose(fp);
getchar();
return 0;
}

Чтение символа из файла :

char fgetc(поток);


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

Запись символа в файл :

fputc(символ,поток);

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

Функции fscanf() и fprintf() аналогичны функциям scanf() и printf() , но работают с файлами данных, и имеют первый аргумент - указатель на файл.

fscanf(поток, "ФорматВвода" , аргументы);