Потоковый ввод-вывод в файлы

Язык C++ / Потоковый ввод-вывод в файлы

 

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

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

Для работы с файлами необходимо подключить заголовочный файл <fstream>. В нем определены несколько классов и подключены заголовочные файлы

  • <ifstream> - файловый ввод ;
  • <ofstream> - файловый вывод.

Файловый ввод-вывод аналогичен стандартному вводу-выводу, единственное отличие – это то, что ввод-вывод выполнятся не на экран, а в файл.

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

При работе с файлом можно выделить следующие этапы:

  • создать объект класса fstream (возможно, ofstream или ifstream);
  • связать объект класса fstream с файлом, который будет использоваться для операций ввода-вывода;
  • осуществить операции ввода-вывода в файл;
  • закрыть файл.
1
2
3
4
5
6
7
8
9
10
#include <fstream>
using namespace std;
int main()
{
  ofstream fout;
  fout.open("file.txt");
  fout << "Привет, мир!";
  fout.close();
  return 0;
}

В результате будет создан файл
Работа с файлами в C++

Режимы открытия файлов устанавливают характер использования файлов. Для установки режима в классе ios предусмотрены константы, которые определяют режим открытия файлов.

Константа Описание
ios::in открыть файл для чтения
ios::out открыть файл для записи
ios::ate при открытии переместить указатель в конец файла
ios::app открыть файл для записи в конец файла
ios::trunc удалить содержимое файла, если он существует
ios::binary открытие файла в двоичном режиме

Режимы открытия файлов можно устанавливать непосредственно при создании объекта или при вызове метода open().

ofstream fout("file.txt", ios::app);
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() - позиция для вывода

 
Пример на С++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#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++

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#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;
}

Результат выполнения:
Ввод строки таблицы
Ввод строки таблицы
Ввод строки таблицы

Полученный файл данных:
Файл данных


Назад: Язык C++

Комментариев к записи: 17

  • А как записать данные в файл с названием, определяемым самим пользователем? Насколько я понял, в качестве названия файла можно использовать только константу (?)


    • Елена Вставская

      Нет, Вы можете ввести имя файла в ходе выполнения программы.

      char filename[40];
      cout >> "filename: ";
      cin << filename;
      inOut.open(filename, ios::in);

  • извините, я наверно вас уже заколебал, с этой 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];

    Тоже не  работает


  •   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-ая строка


  • Подскажите пожалуйста (нигде найти не могу)
    Как выводить в файл строки сверху.
    было
    То, как зверь, она завоет,
    То заплачет, как дитя,
    Надо вставить сверху строчки, что бы получилось так:
    Буря мглою небо кроет,
    Вихри снежные крутя;
    То, как зверь, она завоет,
    То заплачет, как дитя,
    Только не говорите что для этого отдельный файл создавать надо….:)


    • Если использовать флаг ios::beg

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

      Вместо вставки происходит замена.


      • Елена Вставская

        Я, конечно, не буду говорить, что нужно создавать отдельный файл 🙂
        Но придётся считать всю информацию из файла и где-то её сохранить. Потом открыть файл для записи. Записать недостающие строки сверху, а потом снова выгрузить сохраненное содержимое файла.


        • А если надо  заполнить такую таблицу что бы последние введенные данные были наверху.
          То есть, есть шапка которую нельзя трогать.
          файл 1 «с шапкой»
          файл 2 «с новой строкой»
          файл 3 = файл 1+файл2; // итоговый наш файл
          файл 2 = получили новую строку
          файл 4 = скопировать все с 4 строчки и ниже из файла №3
          файл 3 = встаем на четвертую строчку и добавляем файл №2 с нашей строкой
          файл 3 = копируем все из файла 4.
          Только так можно это реализовать? Как можно установить курсор на 4 строчку?
          ————————————————————————————
          | Фамилия  | Имя  |  Отчество  |   дата рождения  |   хобби   |
          ————————————————————————————
          |_________|_____|__________|_______________|________|
          |_________|_____|__________|_______________|________|
          |_________|_____|__________|_______________|________|
          |_________|_____|__________|_______________|________|


          • Как можно установить курсор на 4 строчку?

            И как можно скопировать все что ниже 4 строчки. Или удалить все что выше 4 строчки.


          • Елена Вставская

            Ответила в тексте статьи



  • Блог программиста

    Хорошая статья, но:

    1) в последнем листинге используются операторы >> и <<, в статье не объясняется как именно они работают;

    2) не упоминается функция getline — это единственный способ считать строку целиком;

    3) в начале упоминается про бинарные файлы, но не описано как с ними работать;

    4) если не всегда, то очень часто, входной файл обрабатывается до тех пор, пока не будут считаны все элементы. В статье нет слова про feof;

    5) что если файл не удалось открыть? — там целый комплекс флажков есть и еще можно объект сравнить с нулем (есть перегруженный оператор для этого, который проверяет набор каких-то флажков) — так очень часто делают;

    6) я не знаю что случится при попытке переместить каретку чтения в файле на недоступную позицию, но думаю, что вылетит исключение. Я могу посмотреть в документации, конечно, но мог бы и у вас прочитать (так-то в документации про seekp тоже написано);

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

    ЗЫ:. вот этот system("cls") я бы лично не использовал (как и system вообще) — некроссплатформенно это… По крайней мере по возможности его стоит избегать, никакой острой необходимости вставлять его в такие учебные статьи я не вижу ((


Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *