1 Стандартные потоки

Понятие потока

Поток - это абстрактное понятие, относящееся к любому передвижению данных от

источника к приемнику.

Чтение данных из потока называется извлечением, а запись в поток- помещением.

Иерархия потоков

Потоки ввода/вывода

В языке С++ с каждым устройством ассоциируется поток ввода/вывода:

  • cin - стандартный поток ввода (клавиатура)
  • cout - стандартный поток вывода (терминал)
  • cerr - стандартный поток ошибок.

При работе с потоками происходит автоматический перевод из типа данных в строку для вывода на экран и из строки в числовой тип при вводе.


using namespace std;
int main() {
   int iVal;
   double dVal;
   cin>>iVal;
   cin>>dVal;
   cout<<iVal<<' '<<dVal<<endl;
}


Для работы со стандартными потоками подключается заголовочный файл iostream

Потоки ввода вывода

Данные разных типов можно смешивать:

int nAge;
..
cout << "You entered " << nAge << " years old" << endl;
..

Ввод/вывод строк

При вводе строк извлечение происходит до ближайшего пробела (или другого стандартного разделителя)

..
char str1[100],str2[100];
cin>>str1>>str2;
..

Для ввода строк целиком используются методы потоков get и getline.

В следующем примере демонстрируется ввод строк целиком

using namespace std;

int main () {
  char name[256], title[256];

  cout << "Enter your name: ";
  cin.getline (name,256); # вводим все символы до перевода строки (макс. 256)
  cout << "Enter your favourite movie: ";
  cin.getline (title,256);
  cout << name << "'s favourite movie is " << title;
  return 0;
}

Ввод вывод строк

Для обеспечения безопасного ввода может применяться манипулятор setw:

using namespace std;

int main()
{
   char buf[10];
   cin>>setw(10)>>buf; // не более 10 символов!
   cout<<buf;
   return 0;
}

Циклический ввод-вывод

int main()
{
    char ch;
    while (cin >> ch)
        cout << ch;
    return 0;
}


Данный цикл позволяет ввести строку, а потом очищает ее от разделителей при выводе.

Циклическии ввод-вывод

int main()
{
    char ch;
    while (cin.get(ch))
        cout << ch;
    return 0;
}


Здесь осуществляется посимвольный ввод/вывод вместе с разделителями

Форматирование

С помощью механизма флагов можно управлять параметрами вывода данных на консоль.

Устанавливается значение флага через метод setf, а снимается с помощью unsetf.


cout.unsetf(ios::dec); 
cout.setf(ios::hex);   
cout << 27 << endl;


int a=10;
cout.setf(ios::right);
cout.width(20);
cout<<a<<endl;

Вывод логических величин в текстовом виде:

bool a=true;
cout<<a<<endl;
cout.setf(ios::boolalpha);
cout<<a<<endl;

Основные флаги форматирования:

  • left - левое выравнивание
  • right - правое выравнивание
  • boolalpha - вывод логических значений в текстовом виде
  • dec - основание системы счисления 10
  • oct - основание системы счисления 8
  • hex - основание системы счисления 16
  • showbase - выводить индикатор системы счисления
  • showpos - показывать + для положительных чисел
  • scientific - экспонециальная форма вещественного числа
  • fixed - фиксированная форма вещественного числа

Помимо флагов, изменяющих состояние потока можно использовать манипуляторы.

Идея манипуляторов состоит в том, что их можно помещать в поток вместе с данными.

Многие манипуляторы повторяют названиями флаги.


Для работы с манипуляторами нужно подключить заголовочный файл iomanip.

  • setw() - установить ширину поля
  • setprecision() - количество цифр в дробной части
  • left - выравнивание по левой границе
  • right - выравнивание по правой границе
  • boolalpha - вывод логических значений в текстовом виде
  • nobool alpha - вывод логических значений в числовом виде
  • dec - десятичная система счисления
  • oct - восьмеричная система счисления
  • hex - шестнадцатиричная система счисления
  • showbase - показывать признак системы счисления
  • noshowbase - не показывать признак системы счисления
  • showpos - выводить + для положительных чисел
  • noshowpos - не выводить + для положительных чисел
  • sceintific - экспоненциальная форма для вещественных чисел
  • fixed - фиксированная форма
  • setfill() - установить символ заполнитель пустых элементов поля

2 Файловые потоки

Файлы данных

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


Файлы данных используются для хранения информации в перерывах между сеансами работы пользователей.


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

Порядок работы с файлом

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

  1. Открытие файла (создание потокового объекта).
  2. Проверка результата открытия.
  3. Чтение/запись данных.
  4. Закрытие файла.

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

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

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

Заголовочный файл определяет несколько потоков для работы с файлами

  • ifstream - для чтения данных из файла
  • ofstream - для записи данных в файл
  • fstream - для записи и чтения данных

В следующем примере файл 1.txt открывается для чтения, а 2.txt - для записи:

ifstream in("1.txt");
ofstream out("2.txt");

Открывать файлы можно другим способом.

Сначала создаются объекты файловых потоков, а затем вызывается метод open() файлового потока:

ifstream infile;
infile.open("1.txt");

У данного способа есть два преимущества:

  • Можно снова открыть тот же самый файл после закрытия не создавая новый потоковый объект.
  • Один и тот же потоковый объект может применяться для открытия нескольких файлов (последовательно).

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


Относительное имя

Короткое имя файла в текущем каталоге


Абсолютное (полное) имя

Путевое имя файла в операционной системе

Пример с использованием полного имени в ОС Windows:

ifstream infile("c:\\docs\\projects\\One\\1.txt");

Следует обратить внимание на то, что обратный слэш в строковых константах должен удваиваться.

Пример с использованием полного имени в ОС Unix:

ifstream infile("/home/user/projects/One/1.txt");

У файловых объектов есть функция fail(), которая в случае ошибки открытия возвращает истинное значение.

using namespace std;
int main()
{
   ifstream inf("1.txt");
   if(inf.fail())
   {
      cout<<"Error!"<<endl;
      return 1;
   }
   ...
}

При открытии на чтение, входной файл должен существовать.

При открытии на запись, файлу существовать необязательно, поскольку он все равно будет создан.

Закрытие файла

Для закрытия файла используется функция close().

...
inf.close();
...

При закрытии файла, в который производилась запись, все данные будут записаны на диск.

Чтение данных

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

  • посимвольно (побайтно);
  • поэлементно (до пробела или перевода строки);
  • построчно;
  • поблочно.

Посимвольное чтение

В следующем примере файл читается посимвольно.

using namespace std;
int main() {
   char ch;
   ifstream in("1.txt");
   if(in.fail()) {
      cout<<"Error!"<<endl;
      return 1;
   }
   while(1) {
      ch=in.get();
      if(in.eof())
        break;
      cout<<ch<<endl;
   }
   in.close();
   return 0;
}

Построчное чтение

В следующем примере файл читается построчно.

using namespace std;
int main() {
   char buf[256];
   ifstream in("1.txt");
   if(in.fail()) {
      cout<<"Error!"<<endl;
      return 1;
   }
   while(1) {
      in.getline(buf,256);
      if(in.eof())
        break;
      cout<<buf<<endl;
   }
   in.close();
   return 0;
}

Еще один пример функции, читающей файл построчно:

void fun3()
{
    ifstream file("1.txt");
    char buf[256];

    while(!file.eof())
    {
        file.getline(buf,sizeof(buf));
        cout<<buf<<endl;
    }
    file.close();
}

Поэлементное чтение

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

void fun1()
{
    ifstream file;
    file.open("in.txt"); // файл с числами
    int val;
    while(1)
    {
        file>>val;
        if(file.eof())
          break;
        cout<<val<<endl;
    }
    file.close();
}

Поблочное чтение

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


В следующем примере мы читаем из файла структуру BOOK:

...
BOOK book;
in.read(&book,sizeof(BOOK));
...

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

Запись в файлы

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

void fun2()
{
    ofstream file("out1.txt");
    int count=10;
    while(count--)
       file<<"Hello!"<<endl;
    file.close();
}

Операции get соответствует put, операции read - write.

В следующем примере показано использование операции помещения информации в поток:

int main()
{
     ofstream out("Hello.txt");
     for(int i=0;i<10;i++)
        out<<i+1<<"-Hello, world!"<<endl;
     out.close();
}

В файл 2.txt помещается 10 строк с приветствием и его порядковым номером. При выводе в файл числовых значений они будут преобразованы в строки.

Запись и чтение

Файл можно открыть для записи и чтения:

fstream file("1.txt");

С каждой операцией записи/чтения позиция внутри файла сдвигается на определенное число байт, равное размеру записанных или прочитанных данных. Для изменения текущей позиции надо использовать метод seekp().

file.open("1.txt"); //позиция - начало файла
file.seekp(0,pos::end); //сместиться на конец файла
file.seekp(-10,pos::end); //сместиться на 10 байт к началу от конца файла

Возможные варианты второго параметра seekp()

  1. pos::beg - начало файла
  2. pos::end - конец файла
  3. pos::cur - текущая позиция (любая)

Бинарный и текстовый режимы

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


В текстовом режиме при вводе/выводе символа перевода строки автоматически удаляется/добавляется символ возврата каретки.

В бинарном режиме этого не происходит.


fstream file1("1.txt",ios::binary); //открытие в бинарном режиме