Работа с файлами с использованием конструкций языка Си была рассмотрена здесь.
Для программиста открытый файл представляется как последовательность считываемых или записываемых данных. При открытии файла с ним связывается поток ввода-вывода. Выводимая информация записывается в поток, вводимая информация считывается из потока.
Для работы с файлами необходимо подключить заголовочный файл <fstream>. В нем определены несколько классов и подключены заголовочные файлы
- <ifstream> — файловый ввод ;
- <ofstream> — файловый вывод.
Файловый ввод-вывод аналогичен стандартному вводу-выводу, единственное отличие – это то, что ввод-вывод выполнятся не на экран, а в файл.
Если ввод-вывод на стандартные устройства выполняется с помощью объектов cin и cout, то для организации файлового ввода-вывода достаточно создать собственные объекты, которые можно использовать аналогично этим операторам.
При работе с файлом можно выделить следующие этапы:
- создать объект класса fstream (возможно, ofstream или ifstream);
- связать объект класса fstream с файлом, который будет использоваться для операций ввода-вывода;
- осуществить операции ввода-вывода в файл;
- закрыть файл.
2
3
4
5
6
7
8
9
10
using namespace std;
int main()
{
ofstream fout;
fout.open("file.txt");
fout << "Привет, мир!";
fout.close();
return 0;
}
В результате будет создан файл
Режимы открытия файлов устанавливают характер использования файлов. Для установки режима в классе ios предусмотрены константы, которые определяют режим открытия файлов.
Константа | Описание |
ios::in | открыть файл для чтения |
ios::out | открыть файл для записи |
ios::ate | при открытии переместить указатель в конец файла |
ios::app | открыть файл для записи в конец файла |
ios::trunc | удалить содержимое файла, если он существует |
ios::binary | открытие файла в двоичном режиме |
Режимы открытия файлов можно устанавливать непосредственно при создании объекта или при вызове метода open().
fout.open("file.txt", ios::app);
Режимы открытия файлов можно комбинировать с помощью поразрядной логической операции ИЛИ |, например:
ios::out | ios::in - открытие файла для записи и чтения.
Произвольный доступ к файлу
Система ввода-вывода С++ позволяет осуществлять произвольный доступ с использованием методов seekg() и seekp().
- ifstream &seekg(Смещение, Позиция);
- ofstream &seekp(Смещение, Позиция);
Смещение определяет область значений в пределах файла (long int).
Система ввода-вывода С++ обрабатывает два указателя, ассоциированные с каждым файлом:
- get pointer g - определяет, где именно в файле будет производиться следующая операция ввода;
- put pointer p - определяет, где именно в файле будет производиться следующая операция вывода.
Позиция смещения определяется как
Позиция | Значение |
ios::beg | Начало файла |
ios::cur | Текущее положение |
ios::end | Конец файла |
Всякий раз, когда осуществляются операции ввода или вывода, соответствующий указатель автоматически перемещается.
С помощью методов seekg() и seekp() можно получить доступ к файлу в произвольном месте.
Можно определить текущую позицию файлового указателя, используя следующие функции:
- streampos tellg() - позиция для ввода
- streampos tellp() - позиция для вывода
Пример на С++
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <fstream>
using namespace std;
int main()
{
system("chcp 1251");
system("cls");
char s[80];
fstream inOut;
inOut.open("file.txt", ios::out);
inOut << "строчка текста" << endl;
inOut.seekp(8, ios::beg);
inOut << "еще строчка текста";
inOut.close();
inOut.open("file.txt", ios::in);
inOut.seekg(-6, ios::end);
inOut >> s;
inOut.close();
cout << s;
cin.get();
return 0;
}
В результате выполнения первой части программы будет создан файл
Вторая часть программы выведет в консоль
Ещё один пример. Допустим, нам нужно заполнять таблицу
ФИО | Дата рождения | Хобби |
Причем каждая вновь введенная строка должна размещаться в таблице непосредственно под "шапкой".
Алгоритм решения задачи следующий:
- формируем очередную строку для вывода
- открываем файл для чтения, считываем из него данные и сохраняем их в массив строк
- закрываем файл
- открываем файл для записи
- выводим "шапку" таблицы
- выводим новую строку
- выводим все сохраненные строки обратно в файл, начиная со строки после шапки
Пример на C++
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <fstream>
using namespace std;
#define LINES 100 // максимальное количество строк в файле
int main() {
system("chcp 1251");
system("cls");
char line[LINES][100];
char str[30];
char s[] = "| | | |";
// Ввод данных для размещаемой строки
cout << "ФИО: ";
cin.getline(str, 30); // вводим ФИО
for (int i = 0; str[i] != '\0'; i++) // копируем в строку без 0
s[i + 2] = str[i]; // начиная с указанной позиции
cout << "Дата: ";
cin.getline(str,30);
for (int i = 0; str[i] != '\0'; i++)
s[i + 33] = str[i];
cout << "Хобби: ";
cin.getline(str,30);
for (int i = 0; str[i] != '\0'; i++)
s[i + 50] = str[i];
fstream inOut;
inOut.open("file.txt", ios::in); // открываем файл для ввода
// Считываем из файла имеющиеся данные
int count = 0;
while (inOut.getline(line[count], 100)) count++;
inOut.close(); // закрываем файл
inOut.open("file.txt", ios::out); // открываем файл для вывода
inOut << "--------------------------------------------------------------------------------" << endl;
inOut << "| ФИО | Дата | Хобби |" << endl;
inOut << "--------------------------------------------------------------------------------" << endl;
inOut << s << endl; // выводим сформированную строку
inOut << "--------------------------------------------------------------------------------" << endl;
// Выводим обратно в файл все строки кроме "шапки" (первые 3 строки)
for (int j = 3; j < count; j++)
{
inOut << line[j] << endl;
}
inOut.close();
cin.get();
return 0;
}
Результат выполнения:



Полученный файл данных:
Назад: Программирование микроконтроллеров
Хорошая статья, но:
1) в последнем листинге используются операторы >> и <<, в статье не объясняется как именно они работают;
2) не упоминается функция getline — это единственный способ считать строку целиком;
3) в начале упоминается про бинарные файлы, но не описано как с ними работать;
4) если не всегда, то очень часто, входной файл обрабатывается до тех пор, пока не будут считаны все элементы. В статье нет слова про feof;
5) что если файл не удалось открыть? — там целый комплекс флажков есть и еще можно объект сравнить с нулем (есть перегруженный оператор для этого, который проверяет набор каких-то флажков) — так очень часто делают;
6) я не знаю что случится при попытке переместить каретку чтения в файле на недоступную позицию, но думаю, что вылетит исключение. Я могу посмотреть в документации, конечно, но мог бы и у вас прочитать (так-то в документации про seekp тоже написано);
7) я думаю, стоит упомянуть, что если файл не закрывать — то он закроется сам когда объект будет разрушен. Ну потому, что так-то, мне кажется, файлы не часто закрывают — это нужно делать только если есть вероятность, что программа завершится настолько аварийно, что до деструктора файла дело не дойдет.
ЗЫ:. вот этот system("cls") я бы лично не использовал (как и system вообще) — некроссплатформенно это… По крайней мере по возможности его стоит избегать, никакой острой необходимости вставлять его в такие учебные статьи я не вижу ((
Спасибо Вам огромное. Просто спасение!
Подскажите пожалуйста (нигде найти не могу)
Как выводить в файл строки сверху.
было
То, как зверь, она завоет,
То заплачет, как дитя,
Надо вставить сверху строчки, что бы получилось так:
Буря мглою небо кроет,
Вихри снежные крутя;
То, как зверь, она завоет,
То заплачет, как дитя,
Только не говорите что для этого отдельный файл создавать надо….:)
Если использовать флаг ios::beg
Да, он ставит курсор в начало файла. Но он удалит весь текст который там был. И вставить строчку не получиться.
Вместо вставки происходит замена.
Я, конечно, не буду говорить, что нужно создавать отдельный файл 🙂
Но придётся считать всю информацию из файла и где-то её сохранить. Потом открыть файл для записи. Записать недостающие строки сверху, а потом снова выгрузить сохраненное содержимое файла.
А если надо заполнить такую таблицу что бы последние введенные данные были наверху.
То есть, есть шапка которую нельзя трогать.
файл 1 «с шапкой»
файл 2 «с новой строкой»
файл 3 = файл 1+файл2; // итоговый наш файл
файл 2 = получили новую строку
файл 4 = скопировать все с 4 строчки и ниже из файла №3
файл 3 = встаем на четвертую строчку и добавляем файл №2 с нашей строкой
файл 3 = копируем все из файла 4.
Только так можно это реализовать? Как можно установить курсор на 4 строчку?
————————————————————————————
| Фамилия | Имя | Отчество | дата рождения | хобби |
————————————————————————————
|_________|_____|__________|_______________|________|
|_________|_____|__________|_______________|________|
|_________|_____|__________|_______________|________|
|_________|_____|__________|_______________|________|
Как можно установить курсор на 4 строчку?
И как можно скопировать все что ниже 4 строчки. Или удалить все что выше 4 строчки.
Ответила в тексте статьи
while ( inOut.getline (line[count], 100) ) count++;
А как происходит перебор строк?
Я как понимаю, мы читаем строку в массив line[1];
Потом читаем еще раз строку, но в массив line[2]; и т.д. до line[100].
А где мы говорим, что мы не читаем одну и туже строчку то?
Как встать то на строчку 5? И прочитать что в строке 5 У нас Петров Петр.
Почему если в файле «file.txt» есть информация, то данный код не просто читает он удаляет все из файла?
При считывании строки указатель позиции в файле автоматически смещается на её длину. Поэтому если мы считаем 4 строки, то окажемся на начале 5-ой. Считав её, мы получим нужную нам информацию.
Если данные из файла считываются, то они не удаляются. Удаление данных происходит только при открытии файла в режиме записи (строка 32 последнего примера в статье).
Спасибо большое за ответы. Но а как мне получить строку под номером 4? Допустим я не знаю что в файле, и я хочу прочитать только 4 строчку.
line[4] выводит все четвертые буквы всех строк, но не всю строку целиком.
Может как то можно получить массив строк и массив символов за раз? Например my_line_txt[4][5] — четвертая строка 5 символ.
line[4] — это и есть 4-ая строка. Первый индекс в массиве line[][] — это номер строки, второй — номер символа в строке.
int i;
for(i=0; i<4; i++) inOut.getline(line[i], 100);
inOut.getline(line[i], 100); // вот Ваша 4-ая строка
извините, я наверно вас уже заколебал, с этой 4 строчкой…
Но мне надо прочитать строчку из файла и вывести ее в консоль.
Т.е. В файле есть 4 строка мне ее надо прочитать и вывести на экран.
inOut.open(«file.txt»,ios::out);
for(int i=0; i<4; i++) cout<<line[i];
выводит белиберду.
То есть я хочу теперь эту табличку на экране увидеть, но не всю, а только ее часть, например строку 4.
for(int i=0; i<100; i++)
cout<<line[4][100];
Тоже не работает
for (int i = 0; i < 4; i++) inOut.getline(line[i], 100);
cout << line[3];
А как записать данные в файл с названием, определяемым самим пользователем? Насколько я понял, в качестве названия файла можно использовать только константу (?)
Нет, Вы можете ввести имя файла в ходе выполнения программы.
cout >> "filename: ";
cin << filename;
inOut.open(filename, ios::in);
…
Благодарность!