Хедер fstream предоставляет функционал для считывания данных из файла и для записи в файл. В целом он очень похож на хедер iostream, который работает с консолью, поскольку консоль это тоже файл. Поэтому все основные операции такие же, за мелкими отличиями, как в предыдущей теме по iostream.
Наиболее частые операции следующее:
- Операторы перенаправления ввода\вывода – << и >>
- Методы записи и чтения строк getline() и get() c put()
- Потоковая запись и чтение методами write() и read()
- Методы открытия\создания и закрытия файлов open() и close()
- Методы проверки открыт ли файл is_open() и достигнут ли конец файла eof()
- Настройка форматированного вывода для >> с помощью width() и precision()
- Операции позиционирования tellg(), tellp() и seekg(), seekp()
Это не все возможности, которые предоставляет библиотека fstream. Рассматривать все сейчас мы не будем, поскольку их круг применения достаточно узок. Познакомимся с вышеперечисленными. Начнем с класса чтения.
Класс ifstream
Предоставляет возможности для чтения файлов. Открыть файл можно двумя способами: вызвав метод open() или указав путь к нему в конструкторе. Вам необходимо подготовить текстовый файл, перед тем, как начать набирать код. На диске d создайте папку с именем 1 и в ней создайте файл с расширением txt – “файл.txt”.
1 2 3 4 5 6 7 8 9 | #include <iostream> #include <fstream> // подключаем библиотеку using namespace std; int main() { ifstream file; // создаем объект класса ifstream file.open("d:\\1\\файл.txt"); // открываем файл } |
Открытие файла в конструкторе выглядит так:
1 2 3 4 5 6 7 8 | #include <iostream> #include <fstream> // подключаем библиотеку using namespace std; int main() { ifstream file ("d:\\1\\файл.txt"); // открываем файл в конструкторе } |
Так мы просим открыть файл txt с именем файл.txt, который лежит в папке с названием 1, а папка находится на диске d.
Использование метода open() удобно, если программист не хочет сразу привязываться к файлу. Вдруг нужно свойство класса или глобальную переменную, ну а открывать файл уже потом. Если же нужно открыть файл внутри некой функции, поработать с ним и закрыть, то можно прописать путь к файлу прямо в конструкторе. В общем зависит от ситуации.
Открыв файл, желательно прописать проверку: открылся ли он? Так как есть ряд причин, по которым файл может не открыться, а мы этого не увидим. Например, файла с указанным именем нет в прописанной папке или путь указан неверно. Можно пойти двумя путями: проверить переменную файла в логическом выражении (применив оператор “!”, к примеру) или использовать метод is_open() :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <iostream> #include <fstream> using namespace std; int main() { setlocale(LC_ALL, "rus"); ifstream file ("d:\\1\\файл.txt"); if (!file) { cout << "Файл не открыт\n\n"; return -1; } else { cout << "Все ОК! Файл открыт!\n\n"; return 1; } } |
Так все отработает нормально и файл откроется:
Теперь попробуйте вписать название папки не 1, а 2 ifstream file ("d:\\<span style="color: #ff0000;"><strong>2</strong>\\файл.txt”); и снова запустите программу. Так как папки с указанным именем мы не создавали, то и файл, естественно, не может быть открыт:
Второй вариант проверки с использованием метода is_open() :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <iostream> #include <fstream> using namespace std; int main() { setlocale(LC_ALL, "rus"); ifstream file ("d:\\1\\файл.txt"); if (file.is_open()) // вызов метода is_open() cout << "Все ОК! Файл открыт!\n\n" << endl; else { cout << "Файл не открыт!\n\n" << endl; return -1; } } |
Метод is_open() вернет 1, если файл найден и успешно открыт. Иначе вернет 0 и сработает код прописанный в блоке else.
Если файл не открыт – желательно обработать ошибку. Как правило, если вся работа программы связана с файлом пишут некое сообщение в консоль, и ставят выход из программы. При серьезных ошибках принято возвращать некий код выполнения (число), который будет характеризовать ту или иную ошибку. Коды для каждого вида ошибок автор программы может придумывать свои. Один из способов обработки ошибок в программе мы рассматривали в статье Исключения в С++.
Если файл успешно открыт, из него можно производить чтение.
Оператор считывания >>
Так же как и в iostream считывание можно организовать оператором >>, который указывает в какую переменную будет произведено считывание:
1 2 3 4 5 | double d; int i; string s; file >> d >> i >> s; |
Считает вещественное, целое и строку. Считывание строки закончится, если появится пробел или конец строки. Стоит отметить, что оператор >> применяется к текстовым файлам. Считывание из бинарного файла производить лучше всего с помощью метода read().
Кстати этот оператор достаточно удобен, если стоит задача разделить файл на слова:
1 2 3 | // Считка слов из файла for(file >> s; !file.eof(); file >> s) cout << s << endl; |
Методы getline() и get()
Считывание целой строки до перевода каретки производится так же как и в iostream методом getline(). Причем рекомендуется использовать его переопределеную версию в виде функции, если считывается строка типа string:
1 2 3 4 | //Считка строки из текста string s; getline(file,s); cout << s << endl; |
Если же читать нужно в массив символов char[], то либо get() либо getline() именно как методы:
1 2 3 4 5 6 7 8 9 10 11 12 | int n = 10; //Создаем буффер для чтения char* buffer = new char[n+1]; buffer[n]=0; //Читаем n символов file.get(buffer,n); //Или так, но до первого пробела file.getline(buffer,n,' '); //выводим считанное cout << buffer; //Освобождаем буффер delete [] buffer; |
Принцип в общем тот же, что и в аналогах из iostream: Указывается в параметрах буфер (переменная, куда будет производиться чтение), или точнее указатель на блок памяти (если переменная объявлена статически: char buffer[255] к примеру, то пишется в параметры &buffer), указывается максимальное количество считываемого (в примере это n), дабы не произошло переполнение и выход за пределы буфера и по необходимости символ-разделитель, до которого будет считка (в примере это пробел). Надеюсь я не больно наступлю на хобот фанатикам Си, если сажу что эти две функции на 99% взаимозаменяемы, и на 95% могут быть заменены методом read().
Метод read()
1 2 3 4 5 6 7 8 9 | //Считка из файла N байт int n=10; //Создаем буффер char* buffer=new char[n+1]; buffer[n]=0; //Читаем в него байты file.read(buffer,n); //выводим их на экран cout<<buffer; delete [] buffer; |
Похож на предыдущий пример?
Собственно тут тот же результат – считается указанное количество символов. Исключение только в том, что нельзя указать разделитель. read() применяется для неформатированного ввода. Призван в первую очередь читать бинарные файлы. Поскольку текстовый файл – частный случай бинарного, этот метод вполне применим и к текстовому файлу.
Метод close()
Закрывает файл. Даже добавить нечего. Единственная пожалуй ремарка – от того, что файл, открытый для чтения, не будет закрыт этим методом как правило хуже не станет. Очень редки ситуации, когда открытый для чтения файл портится, если завершить программу не закрывая файл. Связана эта порча прежде всего с нестандартными устройствами типа стримеров на магнитной ленте или каких нибудь потоковых хитрых промышленных контроллерах, но по феншую стоит запомнить – открытый файл должен быть закрыт. Это считается хорошим тоном.
Метод eof()
Проверяет не достигнут ли конец файла. Т.е. можно ли из него продолжать чтение. Выше пример с считкой слов оператором >> как раз использует такую проверку.
Метод seekg()
Производит установку текущей позиции в нужную, указываемую числом. В этот метод так же передается способ позиционирования:
- ios_base::end – Отсчитать новую позицию с конца файла
- ios_base::beg – Отсчитать новую позицию с начала файла (абсолютное позиционирование)
- ios_base::cur – Перескочить на n байт начиная от текущей позиции в файле (по умолчанию)
1 2 3 4 5 | file.seekg(0,ios_base::end); //Стать в конец файла file.seekg(10,ios_base::end); //Стать на 10 байтов с конца file.seekg(30,ios_base::beg); //Стать на 31-й байт file.seekg(3,ios_base::cur); //перепрыгнуть через 3 байта file.seekg(3); //перепрыгнуть через 3 байта - аналогично |
Метод tellg()
Иногда нужно получать информацию о том, сколько уже прочитано. В этом поможет метод tellg():
1 | cout << "Считано байт: " << file.tellg(); |
Он возвращает значение типа int, которое показывает сколько уже пройдено в байтах. Его можно использовать в паре с методом seekg(), чтоб получать размер файла:
1 2 3 4 | //становимся в конец файла file.seekg(0,ios_base::end); //Получаем текущую позицию cout << "Размер файла (в байтах): " << file.tellg(); |
В качестве примера работы методов бинарного чтения можно разобрать такой класс:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | class ReadStream { private: //Приватное поле - файл ifstream f; public: //Конструктор, открывающий файл ReadStream(const char *FileName){ f.open(FileName); if(!f.is_open()) cout<<"Файл не открыт"; } //Метод чтения массива символов-байт с указанной позиции //который возвращает этот массив char *read(int position, int count){ if(!f.is_open()) return 0; f.seekg(position); if(f.eof()) return 0; char *buffer=new char[count]; f.read(buffer,count); return buffer; } //Деструктор, закрывающий файл ~ReadStream(){ f.close(); } }; |
Подобные обертки-классы удобно использовать, если встречается задача читать из бинарного файла целые структуры.
Видео о работе с файлами в С++:
ШЩЗИОКЛДОРПДЛРРОПУЫОРОУИЫЫЕЫУНОУИНЬГНОУЮЬШЫДНГКПГУКОРШГЛУКНРПУКЕНО4ОНЕШГ5О
продам навоз, 1000 рублей 10 кило
Приходи ка мне детка. Позволю погладить маэго барана жиесть.
Большое спасибо за ваши труды!
кака
ti loh