
Тема 8
Использование файловых потоков, векторов, списков и строк стандартной библиотеки С++ |
1 Стандартная библиотека С++ и потоки ввода-вывода
Стандартная библиотека С++ представляет собой коллекцию классов и функций для решения сложных и низкоуровневых задач программирования, в частности, классы для потокового ввода/вывода данных. Консольные приложения имеют доступ к стандартным потокам cin и cout - объектам классов istream и ostream соответственно.
Для работы с потоками чаще всего используются перегруженные операции << (для потоков вывода) и >> (для потоков ввода). Для чтения одного символа может использоваться функция get(). Для чтения строки также может использоваться функция-элемент get с параметрами - указателем на символ, целым (зарезервированный размер буфера) и символ-ограничитель. Функция getline() аналогична get в последнем варианте использования, но при этом разделительный символ удаляется из потока. В приведенном примере вводимая строка воспроизводится на экране. Строка может содержать пробелы. В случае использования >> можно вводить только строку без разделителей (пробелов).
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
char s[100];
cin.getline(s, 100, '\n');
cout << s;
cin.get();
return 0;
}
Для форматирования потоков используются так называемые манипуляторы, которые вставляются между объектами и изменяют состояние потока. Наиболее часто используемые манипуляторы - endl (вставка символа "конец строки" и очистка буфера), ends (вставка символа "конец строки"), flush (очистка буфера потока), hex (преобразование в шестнадцатиричное представление), fixed (использование нотации с фиксированной точкой), left (выравнивание данных по левому краю), right (выравнивание данных по правому краю).
2 Файловый ввод-вывод
Для использования файлового ввода-вывода включается заголовочный файл <fstream>.
Если файл будет использоваться только для вывода, мы определяем объект класса ofstream. Передаваемые конструктору аргументы задают имя открываемого файла и режим открытия. Файл типа ofstream может быть открыт либо - по умолчанию - в режиме вывода (ios_base::out), либо в режиме дозаписи (ios_base::app). Класс ofstream является производным от ostream. Чтобы открыть файл только для чтения, применяется объект класса ifstream, производного от istream. В следующем примере выводится в файл. Файл автоматически создается в текущем каталоге проекта. Как видно из примера, в отличие от консольного вывода, можно свободно использовать символы кириллицы:
#include <fstream>
using namespace std;
int main(int argc, char* argv[])
{
ofstream out("test.txt");
char *s = "Текст выводится в файл!";
out << s; // Можно было вывести текст явно
return 0;
}
Объекты классов ofstream и ifstream разрешено определять и без указания имени файла. Позже к этому объекту можно присоединить файл с помощью функции-члена open(). Чтобы закрыть файл (отключить от программы), вызываем функцию-член close(). Файл можно явно не закрывать, так как он закроется при вызове деструктора потока.
Объект класса fstream (производного от iostream) может открывать файл для ввода или вывода, в том числе одновременно. Например, открытие файл word.out для ввода и дозаписи:
fstream io("word.out", ios_base::in|ios_base::app);
Объект класса fstream можно позиционировать с помощью функций-элементов seekg() (для чтения) или seekp() (для записи). Текущая позиция чтения в файле типа fstream возвращается функциями tellg() или tellp().
3 Использование векторов
Важнейшей составляющей стандартной библиотеки С++ являются контейнеры. Контейнер представляет собой параметризированный класс, хранящий коллекцию других объектов и включающий базовые функции для поддержки использования общих алгоритмов. Простейший вид контейнеров - последовательность. К последовательностям относятся вектор (vector), список (list) и дек (deque, очередь с двумя концами). Наиболее часто используется вектор.
Вектор стандартной библиотеки во многом аналогичен встроенному массиву. Для работы с вектором необходимо включить заголовочный файл vector. Как и все средства стандартной библиотеки, вектор описан в пространстве имен std.
#include <vector>
using std::vector;
При создании переменной типа вектора следует указать параметр - тип данных, которые содержатся в этом векторе. При инициализации вектора можно задавать его размерность:
vector<int> v(n);
Начальное значение числа элементов n может быть как константой, так и переменной, а также любым выражением, результат которого можно привести к целому типу. С помощью функции-элемента resize() изменяется текущая длина вектора.
Доступ к элементам вектора аналогичен доступу к элементам массива. При обращении к несуществующим элементам вектора перегруженная операция индексации не генерирует никаких сообщений об ошибках или исключений. Это сделано для повышения эффективности работы с вектором. Однако существует специальная функция-элемент at(), с помощью которой также можно обращаться к элементам вектора по индексу, однако такая функция проверяет индекс и генерирует исключение, связанное с выходом за пределы диапазона:
vector <double> vd(5);
vd[6] = 10; // Нет никаких сообщений об ошибке
vd.at(7) = 11.5; // Генерация исключения
При выполнении программы размер вектора, как и любого другого последовательного контейнера, можно узнать с помощью функции-элемента size(). В циклах рекомендуется использовать параметры типа unsigned int вместо int, так как функция size() возвращает unsigned int и в противном случае компилятор будет генерировать предупреждение:
for (unsigned int i = 0; i < vd.size(); i++)
vd[i] = i / 2.;
Элементы можно вставлять с конца последовательности с помощью функции-элемента push_back(), которая принимает аргумент того же типа, что и элементы вектора:
vector<int> v;
int elem;
for (int i=0; i < n; i++)
cin >> elem; v.push_back(elem);
С помощью функции pop_back() можно удалить последний элемент.
Вектор может содержать в себе векторы - так можно создать аналог двумерного массива. При инициализации можно задать только число строк, а число столбцов задавать для тех векторов, которые будут помещаться в данный. В следующем примере создается вектор векторов, имитирующий двумерный массив:
vector <vector <int> > a(5); // 5 строк
for (unsigned int i = 0; i < a.size(); i++)
a.resize(6); // 6 столбцов
// Обход элементов вектора:
for (unsigned int i = 0; i < a.size(); i++)
for (unsigned int j = 0; j < a[i].size(); j++)
a[i][j] = i + j;
Проход (итерация) по любому контейнеру может осуществляться путем определения класса итератора, подходящего для данного вида контейнера. Каждый контейнерный класс в стандартной библиотеке С++ способен сгенерировать итератор соответствующий конкретному контейнеру. Итератор обобщает понятие указателя на элемент последовательности. Итератор может быть использован для указания на определенное значение. Пара итераторов может быть использована для определения диапазона или последовательности значений, содержащихся в контейнере.
Контейнер vector<Т> может предоставлять следующие итераторы:
vector<Т>::iterator - итератор, реализующий прямой проход по контейнеру;
vector<Т>::reverse_iterator - итератор, реализующий обратный проход по контейнеру;
vector<Т>::const_iterator - итератор, через который нельзя менять элементы контейнера;
vector<Т>::const_reverse_iterator - константный итератор, реализующий обратный проход по контейнеру.
Итераторы вектора являются итераторами произвольного доступа. Для них реализованы операции сложения с целыми и вычитания целых.
Непосредственно как объект итератор внутри контейнера не содержится, там описан только его тип. Переменную-итератор нужно определять отдельно:
vector<int>::iterator vi;
// v.begin() и v.end() возвращают итератор,
// указывающий на начало и за конец вектора
// Изменяем значение через итератор:
for (vi = v.begin(); vi != v.end(); vi++)
*vi = 200;
Приведенный выше пример может быть также применен к списку и итератору списка.
Итератор const_iterator работает аналогично обычному, за исключением того, что через него нельзя менять элементы массива.
Функция-элемент
insert(iterator pos, const T& x);
вставляет х перед позицией pos. Функция-элемент
erase(iterator first, iterator last);
удаляет подпоследовательность из вектора.