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

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

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

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

Файловой системой называется функциональная часть операционной системы, обеспечивающая выполнение операций над файлами. Примерами файловых систем являются 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(поток, "ФорматВвода" , аргументы);

Работа с текстовыми файлами в 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");

Текстовые файлы

Рассмотрим работу с текстовым файлом в Си на примере. Создайте на диске С текстовый файл с именем TextFile.txt. Наберите в этом файле такие строки:

String_1 123 String_11, 456
String_2
String_3

Сохраните файл.

А это код программы на C, которая открывает наш файл и считывает из него строки:

/* *Author: @author Subbotin B.P..h> #include #define LEN 50 int main(void) { puts("Text file operations"); char cArray; FILE *pTextFile = fopen("C:\\TextFile.txt", "r"); if(pTextFile == NULL) { puts("Problems"); return EXIT_FAILURE; } while(fgets(cArray, LEN, pTextFile) != NULL) { printf("%s", cArray); } fclose(pTextFile); return EXIT_SUCCESS; }

Чтоб открыть текстовый файл в C используем функцию fopen:

FILE *pTextFile = fopen("C:\\TextFile.txt", "r");

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

Строки считываем с помощью функции fgets:

fgets(cArray, LEN, pTextFile);

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

После завершения работы с файлом, его надо закрыть:

fclose(pTextFile);

Получаем:

Русские буквы в строках тоже проходят.

Кстати, эту программу я сделал в Eclipse. Как работать с C/C++ в Eclipse можно посмотреть .

Итак, мы открыли и считали данные из текстового файла.

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

/* Author: @author Subbotin B.P..h> #include int main(void) { FILE *pTextFile = fopen("C:\\TextFileW.txt", "w"); char *cString = "This is a string"; char cNewLine = "\n"; int nVal = 123; if(pTextFile == NULL) { puts("Problems"); return EXIT_FAILURE; } fprintf(pTextFile, "%s%c", cString, cNewLine); fprintf(pTextFile, "%d", nVal); return EXIT_SUCCESS; }

Создаем текстовый файл для записи в него данных:

FILE *pTextFile = fopen("C:\\TextFileW.txt", "w");

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

C-строка cString, и число nVal записываются программой в текстовый файл. cNewLine - это просто переход на новую строку.

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

fprintf(pTextFile, "%s%c", cString, cNewLine);

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

Записывать информацию в текстовый файл мы уже научились. – Если не научились смотрите прошлую статью. Там рассказывается и подробно описано

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

#include

int main ()
{
char s1 // Переменная будет считывать строку
ifstream in (“C:\\\FromC\\myfile.txt” ); // Открываем файл для считывания информации
in >>s1 ; // считываем строку
in .close () // Закрываем файл

cout <Выводим значение s1 на экран
return 0 ;
}

Вот наипростейшая программа для считывания первой строки из текстового файла, который находится по пути
C:\\\FromC\\myfile.txt –
Так как это продолжение прошлой статьи , то я решил использовать тот файл, который мы там создавали. Трудностей с этим, наверное возникнуть не должно.
Но вернемся к коду. Сначала мы открываем файл для считывания из него информации, для этого используем команду ifstream , в скобках указываем либо название файла, либо путь к файлу, как сделано у меня.(“C:\\\FromC\\myfile.txt” );
Когда мы открыли файл, чтобы считать из него что-то, мы объявили одну переменную типа char –
char s1
Теперь нам осталось только присвоить переменной значение строки из файла. Это мы делаем командой in
Обращаем внимание на угловые скобки in >>
Собственно, как должно быть видно из комментариев к коду программы, то чтобы переменная присвоила считываемое значение, мы должны написать её после in >>
in >>s1 ;

Никакой особо сложной задачей это не представляется, особенно если вы уже прекрасно освоили и научились использовать материал прошлой статьи – всё абсолютно аналогично, только 2 команды другие

Создание файла и запись в него информации С++

ofstream out (Имя файла );
out << (Записываемая строка );
out .close ();
=============================

Чтение текста из файла и вывода текста на экран в C++

ifstream in (Имя файла );
in >> (Считываем строку );
in .close (); (Закрываем файл )
============================
Напишем простую программу, которая будет считывать ввод с клавиатуры текста и записывать его в файл:

#include
#include

int main ()
{
\\ 3 будущие строки
clrscsr (); // Очищаем экран

cout <<“Wwedi pervuu stroku” ; cin >>a ; endl ;
cout <<“Wwedi wtoruu stroku” ; cin >>b ; endl ;
cout <<“Wwedi tretuu stroku” ; cin >>c ; endl ;
clrscr (); //

/*Начинаем работу с файлом*/
ofstream out (“C:\\\FromC\\myfile.txt” ); // Открываем файл для записи
out <Записываем первую строчку
out <Записываем вторую строчку
out <Записываем третью строчку
out .close (); // Закрываем файл

//Обнуляем переменные

for (int i =0 ;i <=255 ;i ++)
{a =*“” ; b =*“” ; c =*“” ;}


ifstream in (“C:\\\FromC\\myfile.txt” );
in >>a >>b >>c ; // Считываем каждую новую строчку в новую переменную
in .close (); // Закрываем файл

/* */

for (i =0 ;a !=*“” ;i ++)
{
if (i >sizeof(a )) break ;
cout <

}
cout <<“\n” ; \\

/* */


{
if (i >sizeof(b )) break ;
cout <
}
cout <<“\n” ; \\ Перевели курсор на новую строчку

/* */

for (i =0 ;с !=*“” ;i ++)
{
if (i >sizeof(c )) break ;
cout <<с ;
}

return 0 ;
}
===================

В приведенных выше примерах есть один такой ОГРОМНЫЙ недостаток. Если мы будем пытаться ввести строчку, содержащую пробелы, то программа будет срабатывать не так как нам нужно. Наверное, на эту ошибку наткнулся не только я, но и многие другие люди. Поэтому я оставляю неверно приведенный код, чтобы было видно с чем можно столкнуться.

Так как книжек дома нет, я снова стал рыскать в интернете и понаходил много всякой мудреной ерунды. Но всё-таки как-то подобрал решение своей проблемы.
Помогло то, что читал о том, что cout поддерживает свои методы. И в интернете все советы идут на использование функции getline К моему счастью как использовать эту функцию я нашел очень быстро и потом использовал ее в коде.
Вообще стоит упомянуть и описать эту функцию, но пока что я не особо её понимаю, просто понимаю, что её нужно использовать и понимаю как, поэтому привожу более правильный пример нашей разрабатываемой программы:

#include
#include

int main ()
{
char a ,b ,c ; \\ 3 будущие строки
clrscsr (); // Очищаем экран

/* Вводим значения для переменных*/

cout <<“Wwedi pervuu stroku” ; cin.getline(a,sizeof(a)); endl ;
cout <<“Wwedi wtoruu stroku” ; cin.getline(a,sizeof(b)); endl ;
cout <<“Wwedi tretuu stroku” ; cin.getline(a,sizeof(c)); endl ;
clrscr (); // После ввода значений очистили экран

/*Начинаем работу с файлом*/
ofstream out (“C:\\\FromC\\myfile.txt”); // Открываем файл для записи
out <
Записываем первую строчку
out <Записываем вторую строчку
out <Записываем третью строчку
out .close (); // Закрываем файл

//Обнуляем переменные

for (int i =0 ;i <=255 ;i ++)
{a =*“” ; b =*“” ; c=*“” ;}

/*Продолжаем работу с файлом*/

if stream in (“C:\\\FromC\\myfile.txt” );
in.getline(a,sizeof(a)); // а
in.getline(b,sizeof(b)); // Считываем строчку в переменную b
in.getline(c,sizeof(c)); // Считываем строчку в переменную c
in .close (); // Закрываем файл

/* Считываем посимвольно первую строку и выводим её на экран*/

for (i =0 ;a !=*“” ;i++)
{
if (i >sizeof(a )) break ;
cout <

}
cout <<“\n” ; \\ Перевели курсор на новую строчку

/* Считываем посимвольно вторую строку и выводим её на экран*/

for (i =0 ;b !=*“” ;i ++)
{
if (i >sizeof(b )) break ;
cout <
}
cout <<“\n” ; \\ Перевели курсор на новую строчку

/* Считываем посимвольно третью строку и выводим её на экран*/

for (i =0 ;с !=*“” ;i++)
{
if (i>sizeof (c )) break ;
cout <<с[i];
}

getch (); \\ Ожидаем нажатия клавиши Enter
return 0 ;
}
===================

В этом материале разобран пример посимвольного чтения информации. Так как я не описывал работу с перемнными типа char , то у начинающих могут возникнуть некоторые неудобства воспринятия кода. Просто я не знал, что тип char имеет какие-то особенности и думал всё проще. Поэтому некоторые непонятные моменты приведенной программы можно прочитать в следующей статье работа с char в C++ для начинающих

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

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

Примечание:
break ; – Это команда, которая выполняет выход из цикла. У нас если счетчик цикла for становится больше чем объявленный размер переменной char, то мы принудительно выходим из цикла
!= – это поставленное нами условие. Обозначает такое условие неравенство
if(a !=b ) – Читается как если a не равно b

endl ; – Это перевод курсора на новую строку внутри консоли (насколько я понял)
Эта команда похожа на“\n”

Механизм ввода-вывода, разработанный , не соответствует общепринятому сегодня стилю объектно-ориентированного программирования, кроме того, он активно использует операции с указателями, считающиеся потенциально небезопасными в современных защищённых средах выполнения кода. Альтернативой при разработке прикладных приложений является механизм стандартных классов ввода-вывода, предоставляемый стандартом языка C++.

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

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

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

Enum open_mode { app, binary, in, out, trunc, ate };

Ниже приведены возможные значения флагов и их назначение.

Например, чтобы открыть файл с именем test.txt для чтения данных в бинарном виде, следует написать:

Ifstream file; file.open ("test.txt", ios::in | ios::binary);

Оператор логического ИЛИ (|) позволяет составить режим с любым сочетанием флагов. Так, чтобы, открывая файл по записи, случайно не затереть существующий файл с тем же именем, надо использовать следующую форму:

Ofstream file; file.open ("test.txt", ios::out | ios::app);

Предполагается, что к проекту подключён соответствующий заголовочный файл:

#include

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

If (!file) { //Обработка ошибки открытия файла }

Операторы включения и извлечения

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

File << "Это строка текста";

Можно также записывать текстовую строку по частям:

File << "Это " << "строка " << "текста";

Оператор endl завершает ввод строки символом "возврат каретки":

File << "Это строка текста" << endl;

С помощью оператора включения несложно записывать в файл значения переменных или элементов массива:

Ofstream file ("Temp.txt"); char buff = "Текстовый массив содержит переменные"; int vx = 100; float pi = 3.14159; file << buff << endl << vx << endl << pi << endl;

В результате выполнения кода образуется три строки текстового файла Temp.txt:

Текстовый массив содержит переменные 100 3.14159

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

Оператор извлечения (>>)производит обратные действия. Казалось бы, чтобы извлечь символы из файла Temp.txt , записанного ранее, нужно написать код наподобие следующего:

Ifstream file ("Temp.txt"); char buff; int vx; float pi; file >> buff >> vx >> pi;

Однако оператор извлечения остановится на первом попавшемся разделителе (символе пробела, табуляции или новой строки). Таким образом, при разборе предложения "Текстовый массив содержит переменные" только слово "Текстовый" запишется в массив buff , пробел игнорируется, а слово "массив" станет значением целой переменной vx и исполнение кода "пойдет вразнос" с неминуемым нарушением структуры данных. Далее, при обсуждении класса ifstream , будет показано, как правильно организовать чтение файла из предыдущего примера.

Класс ifstream: чтение файлов

Как следует из расшифровки названия, класс ifstream предназначен для ввода файлового потока. Далее перечислены основные методы класса. Большая часть из них унаследована от класса istream и перегружена с расширением родительской функциональности. К примеру, функция get , в зависимости от параметра вызова, способна считывать не только одиночный символ, но и символьный блок.

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

Ifstream file("Temp.txt"); char buff; int vx; float pi; file.getline(buff, sizeof(buff)); file >> vx >> pi:

Метод getline прочитает первую строку файла до конца, а оператор >> присвоит значения переменным.

Следующий пример показывает добавление данных в текстовый файл с последующим чтением всего файла. Цикл while (1) используется вместо while(!file2.eof()) по причинам, которые обсуждались в .

#include #include using namespace std; int main() { ofstream file; file.open("test.txt",ios::out|ios::app); if (!file) { cout << "File error - can"t open to write data!"; cin.sync(); cin.get(); return 1; } for (int i=0; i<10; i++) file << i << endl; file.close(); ifstream file2; file2.open("test.txt", ios::in); if (!file2) { cout << "File error - can"t open to read data!"; cin.sync(); cin.get(); return 2; } int a,k=0; while (1) { file2 >> a; if (file2.eof()) break; cout << a << " "; k++; } cout << endl << "K=" << k << endl; file2.close(); cin.sync(); cin.get(); return 0; }

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

#include #include using namespace std; int main() { ifstream file; // создать поточный объект file file.open("test.txt"); // открыть файл на чтение if (!file) return 1; // возврат по ошибке отрытия char str; // статический буфер строки // Считывать и отображать строки в цикле, пока не eof while (!file.getline(str, sizeof(str)).eof()) cout << str << endl; // вывод прочитанной строки на экран cin.sync(); cin.get(); return 0; }

Этот код под ОС Windows также зависит от наличия в последней строке файла символа перевода строки, надежнее было бы сделать так:

While (1) { if (file.eof()) break; file.getline(str, sizeof(str)); cout << str << endl; }

Явные вызовы методов open и close не обязательны. Действительно, вызов конструктора с аргументом позволяет сразу же, в момент создания поточного объекта file , открыть файл:

Ifstream file("test.txt");

Вместо метода close можно использовать оператор delete , который автоматически вызовет деструктор объекта file и закроет файл. Код цикла while обеспечивает надлежащую проверку признака конца файла.

Класс ofstream: запись файлов

Класс ofstream предназначен для вывода данных из файлового потока. Далее перечислены основные методы данного класса.

Описанный ранее оператор включения удобен для организации записи в текстовый файл:

Ofstream file ("temp.txt"); if (!file) return; for (int i=1; i<=3; i++) file << "Строка " << i << endl; file.close();

Бинарные файлы

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

Первый параметр методов write и read (адрес блока записи/чтения) должен иметь тип символьного указателя char * , поэтому необходимо произвести явное преобразование типа адреса структуры void * . Второй параметр указывает, что бинарные блоки файла имеют постоянный размер байтов независимо от фактической длины записи. Следующее приложение дает пример создания и отображения данных простейшей записной книжки. Затем записи файла последовательно считываются и отображаются на консоли.

#include #include #include using namespace std; struct Notes { // структура данных записной книжки char Name; // Ф.И.О. char Phone; // телефон int Age; // возраст }; int main() { setlocale(LC_ALL, "Russian"); Notes Note1= { "Грозный Иоанн Васильевич", "не установлен", 60 }; Notes Note2= { "Годунов Борис Федорович ", "095-111-2233 ", 30 }; Notes Note3= { "Романов Петр Михайлович ", "812-333-2211 ", 20 }; ofstream ofile("Notebook.dat", ios::binary); ofile.write((char*)&Note1, sizeof(Notes)); // 1-й блок ofile.write((char*)&Note2, sizeof(Notes)); // 2-й блок ofile.write((char*)&Note3, sizeof(Notes)); // 3-й блок ofile.close(); // закрыть записанный файл ifstream ifile("Notebook.dat", ios::binary); Notes Note; // структурированная переменная char str; // статический буфер строки // Считывать и отображать строки в цикле, пока не eof while (!ifile.read((char*)&Note, sizeof(Notes)).eof()) { sprintf(str, "%s\tТел: %s\tВозраст: %d", Note.Name, Note.Phone, Note.Age); cout << str << endl; } ifile.close(); // закрыть прочитанный файл cin.sync(); cin.get(); return 0; }

В результате выполнения этого кода образуется бинарный файл Notebook.dat из трех блоков размером по 80 байт каждый (при условии, что символы - однобайтовые). Естественно, вы можете использовать другие поточные методы и проделывать любые операции над полями определенной структуры данных.

Класс fstream: произвольный доступ к файлу

Предположим что в нашей записной книжке накопилось 100 записей, а мы хотим считать 50-ю. Конечно, можно организовать цикл и прочитать все записи с первой по заданную. Очевидно, что более целенаправленное решение - установить указатель позиционирования файла pos прямо на запись 50 и считать ее:

Ifstream ifile("Notebook.dat", ios::binary); int pos = 49 * sizeof(Notes); ifile.seekg(pos); // поиск 50-й записи Notes Note; //Notes – описанная выше структура "запись" ifile.read((char*)&Note, sizeof(Notes));

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

Ofstream ofilе ("Notebook.dat", ios::binary | ios::ate); int pos = 49 * sizeof(Notes); ofile seekp(pos); // поиск 50-й записи Notes Note50 = {"Ельцин Борис Николаевич", "095-222-3322", 64}; ofile.write((char*)&Note, sizeof(Notes)); // замена

Если не указать флаг ios::ate (или ios::app), то при открытии бинарного файла Notebook.dat его предыдущее содержимое будет стерто!

Наконец, можно открыть файл одновременно для чтения/записи, используя методы, унаследованные поточным классом fstream от своих предшественников. Поскольку класс fstream произведен от istream и ostream (родителей ifstream и ofstream соответственно), все упомянутые ранее методы становятся доступными в приложении.

В следующем примере показана перестановка первой и третьей записей файла Notebook.dat .

#include #include #include using namespace std; struct Notes { char Name; char Phone; int Age; }; int main() { setlocale(LC_ALL, "Russian"); Notes Note1, Note3; // Открыть файл на чтение/запись одновременно fstream file("Notebook.dat", ios::binary | ios::in | ios::out); file.seekg(2 * sizeof(Notes)); // найти и считать Note3 file.read((char*)&Note3, sizeof(Notes)); file.seekg(0); // найти и считать Note1 file.read((char*)&Note1, sizeof(Notes)); file.seekg(0); // Note1 <== Note3 file.write((char*)&Note3, sizeof(Notes)); file.seekg(2 * sizeof(Notes)); // Note3 <== Note1 file.write((char*)&Note1, sizeof(Notes)); char str; // Считывать и отображать записи в цикле, пока не eof file.seekg(0); // вернуться к началу файла while (!file.read((char*)&Note1, sizeof(Notes)).eof()) { sprintf(str, "%s\tТел: %s\tВозраст: %d", Note1.Name, Note1.Phone, Note1.Age); cout << str << endl; } file.close(); cin.sync(); cin.get(); return 0; }

В конструкторе объекта file надо указать флаги ios::in и ios::out , разрешая одновременное выполнение операций чтения и записи. В результате выполнения этого кода первая и третья записи бинарного файла Notebook.dat поменяются местами.

Дополнительные примеры по теме есть .