NSU Programming Программирование на C++ и Python

Работа с потоками ввода-вывода в C++

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

Программа, выводящая в консоль сообщение Hello, world!, с которой традиционно начинают изучение языка программирования, на языке C++ выглядит следующим образом:

#include <iostream>

using namespace std;

int main() {
    cout << "Hello, world!" << endl;
    return 0;
}

Директива #include и функция main() знакомы читателю по языку C. Способ вывода строки в консоль отличается от стандартной функции printf языка C и даёт нам повод начать разговор про C++.

Глобальный объект cout отвечает за вывод в стандартный поток вывода stdout. Оператор вставки << передает различные объекты в поток вывода. Манипулятор endl выполняет перевод строки. Оператор << позволяет строить цепочки вызовов, которые будут выполняться слева направо: сначала мы вывели строку Hello, world!, а затем манипулятор endl.

Чтобы считать данные из стандартного потока ввода stdin, необходимо воспользоваться объектом cin и оператором извлечения >>.

int a;
double x;

cin >> a >> x;

Здесь мы снова построили цепочку вызовов и получили значения сразу для двух переменных. При обращении к потоку ввода мы не указывали тип данных, которые необходимо прочитать. Оператор >> сам определяет типы объектов и заполняет их из потока ввода.

Объекты cout, cin, а также операторы вставки и извлечения определены в заголовочном файле <iostream>

Работа с файлами

Все операции ввода-вывода в C++ организованы через потоки и операторы << и >>. Мы уже рассмотрели операции ввода-вывода в потоки stdout и stdin. Операции ввода-вывода с файлами устроены схожим образом. Для работы с файловыми потоками необходимо подключить заголовочный файл <fstream>. Следующая программа создает файл test.txt и записывает в него строку Hello, world!

#include <fstream>

using namespace std;

int main() {
    ofstream ofile("test.txt", ios::out);
    if (ofile.is_open()) {
        ofile << "Hello, world!";
    }

    return 0;
}

Сначала мы создали объект типа ofstream. В его конструктор мы передали имя файла test.txt и флаг ios::out, указывающий на то, что мы собираемся осуществлять операции вывода. Всегда необходимо проверять, что операция открытия/создания файла прошла успешно. Если не выполнить эту проверку, то, если по какой-либо причине файл открыть не удалось, дальнейшие шаги приведут к аварийному завершению программы. Метод is_open() позволяет выполнить такою проверку. Дальше идет уже знакомый нам вызов оператора <<, который в этом случае работает с файловым потоком вывода. Обратите внимание, что нет необходимости вручную закрывать файл, если того не требует логика программы. При выходе объекта ofile из области видимости, файл будет корректно закрыт.

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

#include <fstream>
#include <string>
#include <iostream>

using namespace std;

int main() {
    ifstream ifile("test.txt", ios::in);
    if (ifile.is_open()) {
        string line;
        while (ifile >> line) {
            cout << line << ' ';
        }
    }

    return 0;
}

Здесь мы воспользовались файловым потоком ввода ifstream и флагом ios::in. В этой программе мы создали переменную line типа string, чтобы хранить считанные из файла данные. Неочевидным моментом здесь является использование цикла while. Дело в том, что оператор >> считывает символы до тех пор, пока не встретит разделитель (пробел, табуляция или перенос строки). Если бы мы вызвали этот оператор один раз, то в переменную line было бы записано Hello,, а это не то, чего мы хотели. Цикл позволяет прочитать файл до конца.

Если же мы хотим прочитать только одну строчку из файла, то можно воспользоваться функцией getline, определенной в <string>. С этой функцией наша программа примет следующий вид:

#include <fstream>
#include <string>
#include <iostream>

using namespace std;

int main() {
    ifstream ifile("test.txt", ios::in);
    if (ifile.is_open()) {
        string line;
        getline(ifile, line);
        cout << line << end;
    }

    return 0;
}

Наконец, для чтения символов из потока по одному можно использовать метод get()

char c;
while (ifile.get(c)) {
    cout << c;
}

Аналогичный метод есть и у объекта cin.

По умолчанию файловые потоки работают в текстовом режиме, т.е. передают и принимают строковые символы. В некоторых случаях необходимо работать непосредственно с последовательностью байт, которые не должны быть интерпретированы как строковые символы. Для таких случаев существует флаг ios::binary. Детали работы с бинарными файлами смотрите в документации.

Строковые потоки

Часто бывает удобно работать со строковыми потоками. Инструменты для работы со строковыми потоками подключаются с помощью заголовочного файла <sstream>. Строковые потоки позволяют удобно инициализировать объекты различных типов из их текстового представления. Представим себе, что мы получили географические координаты НГУ в виде строки "(54.847830, 83.094392)". Наша задача извлечь из строки две величины типа double. Сделать это можно следующим образом:

#include <string>
#include <sstream>
#include <iostream>

using namespace std;

int main() {
    string nsucoor("(54.847830, 83.094392)");

    stringstream ss(nsucoor);
    double lat, lon;
    ss.ignore(1);  // skip '('
    ss >> lat;
    ss.ignore(2);  // skip ", "
    ss >> lon;
    cout << lat << ", " << lon << endl;

    return 0;
}

Резюме

Мы обсудили, что все операции ввода-вывода в С++ реализованы единообразно с помощью потоков. Вывод в поток осуществляется с помощью оператора вставки <<, ввод из потока осуществляется с помощью оператора извлечения >>. Мы рассмотрели три типа потоков: стандартные, файловые и строковые. Этого достаточно для уверенного начала работы с потоками ввода-вывода в C++.

Документация