При форматированном вводе/выводе числа хранятся на диске в виде серии текстовых символов. Например, число 6.02 вместо того, чтобы храниться в виде четырёхбайтного значения типа float или восьмибайтного double, хранится в виде последовательности символов '6', '.', '0', '2'. Поэтому при вводе последовательности числовых значений их надо разделять (например, пробелами). Рассмотрим пример, демонстрирующий содание файла и запись в него информации.
#include <iostream>
#include <fstream>
#include <conio>
using namespace std;
int main() {
float f=6.02;
int i=5;
char str[]="Privet!";
ofstream fout("c:\\os\\proba.txt"); // fout - объект класса ofstream (имя выходного потока)
fout << str << ' ' << i << ' ' << f; // вывод в файл (Privet! 5 6.02)
getch(); return 0;
}
Здесь, мы определили объект fout, т.е. грубо говоря дали имя выходному потоку и инициализировали его файлом c:\\os\\proba.txt. По умолчанию, если файл не существует, он создаётся. Если файл уже существует, он переписывается – новые данные заменят старые. В данной программе я умышленно дал имя fout объекту, чтобы выявить аналогию с cout, поскольку данная программа предназначена для вывода информации в файл, а cout – для вывода данных на экран (на консоль). Когда программа завершается, объект fout вызывает свой деструктор, который закрывает файл для доступа (т.е. удаляет поток). В данной программе для разделения данных мы выбрали пробелы, хотя для этих целей можно использовать любые разделители (например, \n).
Теперь необходимо рассмотреть механизм считывания данных из файла. Одним из распространённых способов считывания является использование объекта класса ifstream, инициализированного именем файла и действующего по принципу cin. Рассмотрим пример, демонстрирующий считывание данных из файла, созданного предыдущей программой.
#include <iostream>
#include <fstream>
#include <conio>
using namespace std;
int main() {
float f;
int i;
char str[8];
ifstream fin("c:\\os\\proba.txt");
fin >> str >> i >> f;
cout << str << " " << f << " " << i;
getch(); return 0;
}
Здесь, мы создали объект fin класса ifstream и инициализировали его именем файла. При считывании данных все файловые символы приводятся к тем типам данных, которые мы указали при определении переменных. Если строку для считывания мы изменим на fin >> str >> f >> i; , то вместо 6.02 увидим 6.
При считывании данных из файла, необходимо понимать для какой цели происходит считывание и каков формат файла. Вообще, форматированный файловый ввод/вывод можно представить себе, как запись определённой информации на лист бумаги, где каждая единица информации представляет собой просто символ, записанный с учётом предложенного вами формата. Таким образом вы должны знать, каков формат записываемой или считываемой информации, т.е. в каком месте, на каких строках и в какой последовательности записаны те или иные данные. Иначе вы не сможете создать работающий алгоритм считывания "правильных данных".
Из приведённого нами примера считывания текстового файла видно, что при таком считывании мы получаем возможность в дальнейшем оперировать считанными данными. Например, мы можем продолжить данную программу, введя дополнительную переменную и посчитав сумму считанных чисел.
Рассмотрим следующий пример. Допустим, необходимо написать программу, позволяющую считывать данные из указанного пользователем файла и просто выводить их на экран.
#include <iostream>
#include <fstream>
#include <conio>
using namespace std;
int main() {
const int MAX=100;
char filename[40], str[MAX];
cout << "Vvedite imya faila: "; cin >> filename;
ifstream fin(filename);
if(!fin) cout << "\nOshibka okrytiya faila!"; // проверка потока на ошибки
else {
while(!fin.eof()) { fin.getline(str, MAX); cout << str << endl; }
}
getch(); return 0;
}
Данная программа отличается в лучшую сторону от предыдущих примеров тем, что в ней имеется проверка на наличие ошибок при открытии файла. Условие отсутствия ошибок может записываться различными способами, но наиболее распространёнными считаются следующие:
- if(fin.good()) // если нет ошибок
- if(fin) // если нет ошибок
Посредством цикла мы осуществляем построчное считывание файла до тех пор, пока не встретим признак конца файла (EOF). Сигнал EOF посылается в программу операционной системой, когда больше нет данных для чтения. Недостаток этого алгоритма считывания строки из файла заключается в том, что строковые данные из файла считываются построчно и по завершении работы цикла переменная str будет содержать только последнюю строку файла. Для случая, когда нам необходимо, чтобы переменная str содержала все строковые данные файла, тем самым предоставляя возможность преобразования и модификации этих данных, нужно изменить алгоритм считывания следующим образом.
int main() {
…
else {
fin.getline(str, MAX, '\0'); // EOF='\0'
cout << str << endl;
cout << strlen(str); //для доказательства, что переменная str содержит всю информацию из файла
}
getch(); return 0;
}
Здесь необходимо обратить внимание на то, что вместо цикла с построчным чтением, мы применили функцию getline(str, MAX, '\0'), позволяющую за один раз считать всю информацию из файла до EOF и передать её в переменную str. Обратите внимание и на тот факт, что в большинстве случаев все строки текстовых файлов оканчиваются символом перевода строки '\n'. Данная реализация программы выгодно отличается от предыдущей тем, что данные считанные из файла пригодны для дальнейших преобразований.
Для ввода/вывода единичных символов можно использовать функции put() и get(), являющиеся методами ostream и istream соответственно. Функцию put() удобно применять для посимвольной записи в файл, а функцию get() – для посимвольного считывания из файла. Приведём пример, демонстрирующий посимвольное считывание.
int main() {
char filename[40], sim, str[100];
int i=0, j;
cout << "Vvedite imya faila: "; cin >> filename;
ifstream fin(filename);
if(fin) {
while(!fin.eof()) {
fin.get(sim);
str[i]=sim;
i++;
}
for(j=0; j<(i-1); j++) cout << str[j]; //можно осуществлять любые преобразования
}
else cout << "Oshibka okrytiya faila!";
getch(); return 0;
}
Помимо использования функций getline() и get(), есть очень удобный способ чтения символов из текстового файла – использовать функцию rdbuf(), которая возвращает указатель на объект, в котором находится буфер символов, считанных из потока. Приведём пример программы, позволяющей просто выводить содержимое текстовых файлов.
…
#include <fstream>
using namespace std;
int main() {
char filename[40];
cout << "Vvedite imya faila: "; cin >> filename;
ifstream fin(filename);
if(fin) {
cout << fin.rdbuf() << endl;
}
else cout << "Oshibka okrytiya faila!";
getch(); return 0;
}
Пожалуй, это самая короткая программа чтения файлов. Обратите внимание на то, что функция rdbuf() автоматически прекращает работу при достижении EOF. В этом примере отсутствует возможность дальнейшего оперирования считанными из файлов строковыми данными. Мы не сможем производить над ними какие бы то ни было операции по изменению или модификации.
Необходимо акцентрировать внимание на том, что для закрытия потока определён метод close() (например, fin.close();). Его явный вызов необходим только тогда, когда требуется закрыть поток раньше конца его области видимости. Это бывает актуальным в том случае, когда уже один раз считана информация из файла и необходимо повторно её считать. Если после первого считывания поток не закрыть, то при повторном считывании данные могут быть неверными.
Д/З
| 1) Напишите программу, позволяющую вводить динамический целочисленный массив и записывать его в текстовый файл. После записи формат файла должен быть следующим, например:
Celochisl. massiv iz 5 elementov
1 -2 3 7 4
2) Напишите другую программу, позволяющую: а) считывать файл, созданный предыдущей программой; б) определять размерность записанного массива, считывать массив посредством динамического выделения памяти и выводить на экран сумму его элементов. В первой версии программы размерность массива должна определяться путём распознания первой строки файла, во второй версии – строки, хранящей числовые значения элементов массива.
|
Ответ
| 1
#include <iostream>
#include <fstream>
#include <conio>
using namespace std;
int main() {
int N, i;
cout << "Vvedite kol-vo elementov cel. massiva: "; cin >> N;
int *mas = new int [N];
for(i=0; i<N; i++) {
cout << "Vvedite element " << i << ": ";
cin >> mas[i];
}
ofstream fout("c:\\os\\massiv.txt");
if(fout) {
fout << "Celochisl. massiv iz " << N << " elementov:" << endl;
for(i=0; i<N; i++) fout << mas[i] << ' ';
cout << "\nDannye uspeshno zapisany v fail!";
}
else cout << "\n Oshibka otkrytiya faila!";
getch(); return 0;
}
2.1
#include <iostream>
#include <fstream>
#include <conio>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
using namespace std;
int main() {
const int Max=40;
char filename[Max], str[Max];
int N=0, i, j=0, K=0, sum=0;
cout << "Vvedite imya faila: "; cin >> filename;
ifstream fin(filename);
if(fin) {
fin.get(str, Max, '\n');
for(i=0; i<strlen(str); i++) {
if(isdigit(str[i])) K++;
}
char *strd = new char [K+1];
for(i=0; i<strlen(str); i++) {
if(isdigit(str[i])) { strd[j]=str[i]; j++; }
strd[j] = '\0';
}
N=atoi(strd); //преобразовываем строку, описыв. число, в целое число
int *m = new int [N];
for(i=0; i<N; i++) { fin >> m[i]; sum=sum + m[i]; }
cout << "Iz faila " << filename << " schitano "
<< N << " elementov massiva: ";
for(i=0; i<N; i++) cout << m[i] << " ";
cout << "\n Ih summa = " << sum;
}
else cout << "\n Oshibka otkrytia faila!";
getch(); return 0;
}
2.2
#include <iostream>
#include <fstream>
#include <cstring>
#include <conio>
using namespace std;
int main() {
const int Max=100;
char filename[Max], str[Max];
int i, j=0, K=0, sum=0, np;
cout << "Vvedite imya faila: "; cin >> filename;
ifstream fin(filename);
if(fin) { //если нет ошибок
fin.getline(str, Max, '\0'); //записываем в str всё содержимое файла
for(i=0; i<strlen(str); i++) {
if(str[i]=='\n') { np=i; break; } // определяем № позиции конца 1-ой строки
}
for(j=(np+1); j<strlen(str); j++) { // начиная с начала 2-ой строки,
if(str[j]== ' ') K++; // считаем кол-во пробелов между
} // элементами массива
fin.close(); // закрываем поток, поскольку все данные уже считаны в str
}
else { cout << "\n Oshibka otkrytia faila!"; getch(); return 1; }
ifstream fin2(filename); //определяем второй поток с этим файлом
if(fin2) {
fin2.getline(str, Max);
int *m = new int [K]; //учитывая, что кол-во пробелов = кол-ву чисел
for(i=0; i<K; i++) { fin2 >> m[i]; sum=sum + m[i]; }
cout << "\nIz faila schitano " << K << " elementov massiva: ";
for(i=0; i<K; i++) cout << m[i] << " ";
cout << "\n Ih summa = " << sum;
}
else cout << "\n Oshibka otkrytia faila!";
getch(); return 0;
}
|