C++. Сборник рецептов.
10.1. Вывод выровненного текста.
Проблема.
Требуется вывести текст, выровненный по вертикали. Например, если ваши данные представлены в табличном виде, вам захочется, чтобы они выглядели следующим образом.
Jim Willсох Mesa AZ.
Bill Johnson San Mateo CA.
Robert Robertson Fort Collins CO.
Кроме того, вам, вероятно, захочется иметь возможность выравнивать текст вправо или влево.
Решение.
Используйте определенные в <ostream> типы ostream или wostream для узких или широких символов и стандартные манипуляторы потоков для установки размера полей и выравнивания текста. Пример 10.1 показывает, как это можно сделать.
Пример 10.1. Вывод выровненного текста.
#include <iostream>
#include <iomanip>
#include <string>
Using namespace std;
Int main() {
ios_base::fmtflags flags = cout.flags();
string first, last, citystate;
int width = 20;
first = "Richard";
last = "Stevens";
citystate = "Tucson, AZ";
cout << left // Каждое поле выравнивается влево.
<< setw(width) << first // Затем для каждого поля.
<< setw(width) << last // устанавливается его ширина и.
// записываются некоторые данные.
<< setw(width) << citystate << endl;
cout.flags(flags);
}
Вывод выглядит следующим образом.
Richard Stevens Tucson, AZ.
Обсуждение.
Манипулятор — это функция, которая выполняет некоторую операцию над потоком. Применяемые к потоку манипуляторы задаются в операторе operator<<. Формат потока (ввода и вывода) задается набором флагов и установочных параметров конечного базового класса потока, ios_base. Манипуляторы обеспечивают удобный способ настройки этих флагов и установочных параметров без явного использования для этой цели функций setf или flags, которые громоздки и воспринимаются с трудом. Для форматирования потока вывода лучше всего использовать манипуляторы.
В примере 10.1 используется два манипулятора для вывода текста в две колонки. Манипулятор setw задает размер поля, a left обеспечивает выравнивание влево находящегося в поле значения (дополнением манипулятора left, что неудивительно, является right). Когда вы используете слово «поле», вы просто говорите, что хотите дополнить заполнителем выдаваемое в поле значение с одной или с другой стороны, чтобы только ваше значение выводилось в этом поле. Если, как в примере 10.1, вы выравниваете значение влево и затем задаете размер поля, следующее записываемое в поток значение будет начинаться с первой позиции этого поля. Если переданные в поток данные имеют недостаточный размер и не могут заполнить все пространство поля, то правая часть поля будет дополнена символом заполнителя потока, которым по умолчанию является одиночный пробел. Вы можете изменить символ заполнителя с помощью манипулятора setfill.
Myostr << setfill('.') << "foo";
Если помещаемое в поле значение превышает его размер, будет напечатано все значение и никаких дополнительных символов выводиться не будет.
Табл. 10.1 содержит краткое описание манипуляторов, работающих с любыми типами значений (текстом, числами с плавающей точкой, целыми числами и т.д.). Имеется ряд манипуляторов, которые применяются только при выводе чисел с плавающей точкой — они описываются в рецепте 10.2.
Табл. 10.1. Текстовые манипуляторы.
| Манипулятор | Описание | Пример вывода |
|---|---|---|
| left right | Выровнять значения в текущем поле вправо или влево, заполняя незанятое пространство символом-заполнителем | Выравнивание влево apple banana cherry Выравнивание вправо (ширина поля 10) apple banana cherry |
| setw(int n) | Установить размер поля на n символов | См. предыдущий пример |
| setfill(int с) | Использовать символ с для заполнения незанятого пространства поля | cout << setfill('.') << setw(10) << right << "foo" Выдает: .......foo |
| boolalpha noboolalpha | Отобразить булевы значения в текущем локализованном представлении слов true и false, а не 1 и 0 | cout << boolalpha << true Выдает: true |
| endl | Записать в поток символ новой строки (newline) и очистить буфер вывода | Нет |
| ends | Записать в поток null-символ ('\0') | Нет |
| flush | Очистить буфер вывода | Нет |
Некоторые представленные в табл. 10.1 (и в табл. 10.2 в следующем рецепте) манипуляторы переключают бинарные флаги потоков и в действительности реализуются как два манипулятора, которые включают и отключают флаг. Например, возьмем манипулятор boolalpha. Если вы хотите, чтобы булевы значения отображались в соответствии с текущей локализацией (например, «true» и «false»), используйте манипулятор boolalpha. Для отключения этого режима, чтобы вместо слов печатались 0 и 1, используйте манипулятор noboolalpha (он используется по умолчанию).
Действие всех манипуляторов сохраняется до тех пор, пока оно не будет явно изменено, исключая манипулятор setw. Из примера 10.1 видно, что он вызывается перед каждой записью, однако left используется только один раз. Это объясняется тем, что ширина поля устанавливается в нуль после записи каждого значения в поток при помощи оператора operator<<; чтобы обеспечить одинаковую ширину всех полей, мне пришлось каждый раз вызывать setw.
Стандартные манипуляторы позволяют делать многое, но не все. Если у вас возникает потребность в написании собственного манипулятора, см. рецепт 10.2.
Как и все другие классы стандартной библиотеки, работающие с символами, манипуляторы работают с потоками узких или широких символов. Поэтому вы можете использовать их в шаблонах для написания утилит форматирования, обрабатывающих потоки символов любого вида. В примере 10.2 приводится шаблон класса TableFormatter, который форматирует данные в колонки одинаковой ширины и выдает их в поток.
Пример 10.2. Параметрический класс для табличного представления данных.
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
Using namespace std;
// TableFormatter выдает в поток вывода символы типа T в форматированном.
// виде.
Template<typename T>
Class TableFormatter {
Public:
TableFormatter(basic_ostream<T>& os) : out_(os) {}.
~TableFormatter() {out_ << flush;}.
template<typename valT>
void writeTableRow(const vector<valT>& v, int width);
//...
Private:
basic_ostream<T>& out_;
};
Template<typename T, // ссылается на список параметров шаблона класса.
typename valT> // ссылается на список параметров функции-члена.
Void TableFormatter<T>::writeTableRow(const std::vector<valT>& v,
int width) {
ios_base::fmtflags flags = out_.flags();
out_.flush();
out_ << setprecision(2) << fixed; // Задать точность в случае применения.
// чисел с плавающей точкой.
for (vector<valT>::const_iterator p = v.begin(); p != v.end(); ++p).
out_ << setw(width) << left << *p; // Установить ширину поля, его.
// выравнивание и записать элемент.
out_ << endl; // Очистить буфер.
out setf(flags); // Восстановить стандартное состояние флагов.
}
Int main() {
TableFormatter<char> fmt(cout);
vector<string> vs;
vs.push_back("Sunday");
vs.push_back("Monday");
vs.push_back("Tuesday");
fmt.writeTableRow(vs, 12);
fmt.writeTableRow(vs, 12);
fmt.writeTableRow(vs, 12);
vector<double> vd;
vd.push_back(4.0);
vd.push_back(3.0);
vd.push_back(2.0);
vd.push_back(1.0);
fmt.writeTableRow(vd, 5);
}
Вывод представленной в примере 10.2 программы выглядит следующим образом.
Sunday Monday Tuesday.
4.00 3.00 2.00 1.00.
Смотри также.
Таблица 10.1, рецепт 10.2.
- 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.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.