LEM: C++ библиотека поисковой машины - форматтер вывода - класс OFormatter

 

Файлы

Общее описание

Флаги форматирования

Коды цветов

Кодовые страницы

Формирование utf-8, utf-16 файлов

Как происходит расцвечивание

 

Классы

Базовый класс OFormatter реализует всю логику форматирования. Класс OUFormatter расширяет его функциональность за счет двух моментов:

1. Конструктор открывает текстовый файл в кодировке utf-8

2. Юникод-символы пишутся в выходной поток напрямую, без перекодирования в ASCII. Кроме очевидного расширения универсальности за счет вывода юникода отсутствие перекодирования увеличивает быстродействие.

В поисковой утилите φaind вывод на консоль выполняется именно с помощью класса OUFormatter, если используется ОС Windows NT. Благодаря этому результаты поиска на многих европейских языках можно печатать в консоли одновременно, без переключения кодовой страницы (см. примеры).

Файлы

Объявлен в файле oformatter.h. Реализация класса - в файле lem\io\oformatter.cpp.

 

Общее описание

Класс OFormatter предназначен для замены стандартного пакета форматного вывода информации в текстовые потоки (терминал и файлы) из C, то есть процедур ...printf. Необходимость такой замены обусловлена значительным расширением стандартного набора типов данных в Библиотеке, в частности, присутствием типов BaseString<...>, BaseFString<...> других. Значительно расширены возможности форматирования выводимых полей. Автоматически осуществляется настройка на текущий тип REAL, так что в программах нет надобности перебивать спецификации %e на %le и так далее, достаточно указания %re (подробнее см. далее). Идеология C-процедур printf уже достаточно близка к форматному выводу информации в языке ФОРТРАН (если кто не знает - древнейший язык программирования, вопреки модным штучкам типа Java широко используется в научных и инженерных расчетах). Одно из важнейших достоинств такого способа вывода данных - чрезвычайная компактность описания весьма сложных законов форматирования, поэтому мы значительно расширили не только диапазон стандартных выводимых типов, но и добавили специальные флаги форматирования, например, выравнивание на границу поля и так далее.

Следует заметить, что ряд редко используемых спецификаций из ...printf исключен, в частности, %x. Далее, вместо %e, %le, %f, %lf, %g, %lg используется другой набор спецификаторов, более приспособленных для нашей Библиотеки, хотя можно свободно использовать и %e, %f, %g.

Без изменений осталось использование спецификаторов %%, %c, %s, %d, %ld. Впрочем, значительно расширены возможности вывода (форматирования) целых чисел.

Так как вывод текста производится в 'виртуальный файл' BaseStream, то не составляет труда реализовать специальные режимы вывода - достаточно написать класс-потомок BaseStream и перегрузить в нем часть методов (с подробностями лучше знакомиться в "lem_bstr.h", см. также описание потоков).

Однако, в целом расширение возможностей OFormatter ограничивается принятым механизмом передачи параметров через стек. Недостаток такого подхода - сложность расширения возможностей по выводу новых типов данных. Поэтому класс OFormatter реализует и второй подход, а именно тот, что применен в библиотеке IOStream языка C++. То есть, мы переопределили операторы << для всех базовых стандартных типов Библиотеки, а прикладной программист может написать необходимое число дополнительных функций OFormatter& operator<<(...) для своих типов. Однако необходимо отметить, что управление режимами форматирования для такого подхода производится не столь просто, как для печати через метод OFormatter::printf. Кстати, можно совершенно свободно перемежать вывод через OFormatter::printf и operator<<, без какой-либо синхронизации потоков (это необходимо в IOStream C++).

Чтобы предельно облегчить использование OFormatter, вместо стандартного printf, для вывода на терминал в прикладных модулях доступен объект mout (объявлен в хидере lem_coap.h) В результате, строка кода

printf( "Hello, world!\n" );

должна быть заменена на

mout.printf( "Hello, world!\n" );

Также доступен объект merr для вывода сообщений об ошибках (стандартный поток stderr).

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

OFormatter mfile( "filename.txt" );
mfile.printf( "Hello, disk file!\n" );

При определенном макросе LEM_NOREAL исключаются (становятся недействительными) все спецификации, обслуживающие типы REAL/REAL4/... (подробности - в модуле "lem_real.h").
 

Флаги форматирования

Общий вид

Каждое поле имеет общий вид (разберем на примере %+10.6W20Ace):

% +10.6 W20 Ac e
специальный
символ
число выводимых цифр (общее число 10 цифр и после запятой 6 цифр) и режим вывода знака - печатать + или - длина суперполя - 20 символов режим размещения в суперполе - по центру тип считываемого со стека значения - текущий REAL

Поля спецификации

%XYYY.ZZZZ

где:

ZZZZ Число цифр мантиссы после десятичной точки, выводимых в поток.

YYY Общая длина поля.

X - Поле для указания способа указания знака числа: X = '+' - печатается '+' или '-' соответственно. X = ' ' - печатается ' ', если число>0, иначе печатается '-'. По умолчанию печатается только '-', когда число <0.

%...Xn... - основание системы счисления будет равно 'n' для след. поля.

%...Wn... - длина суперполя будет равна n.

%...An... - устанавливается тип выравнивания для поля в суперполе. Al - выравнивание к левому краю. Ac - центрирование. Ar - выравнивание к правому краю.

%...Ln... - преобразование символов. Lc - все к большим. Ls - все к малым.

%...Knnn... - строковый аргумент, который будет прочитан со стека (обычно через спецификацию s), будет в кодировке nnn (например 1251 - кодировка MS Windows). После считывания строка будет автоматически перекодирована в 866 кодировку MSDOS.

%...in... - подготовленное символьное представление поля выводится подряд n раз, к стеку аргументов обращение производится только один раз.

%...Fnm... - специальный флаг управления вывода поля с обозначением 'n' устанавливается в состояние 'm'. Значение флагов и их состояний определяется конкретно для разных типов. Будучи установлен в, определенное стстояние, флаг продолжает действовать при выводе всех следующих полей до того момента, когда другая команда %Fnm изменит его значение.

%...^n... - число знакомест под порядок числа с плавающей точкой.

%...n... - тип нормальной длины ( REAL8, INT16 ).

%...l... - тип с приставкой 'long' ( long int, REAL10 )

%...u... - тип с приставкой 'unsigned' ( unsigned int, unsigned long )

%...q... - тип с приставкой 'quatro' ( int64, REAL18 )

%v - изменение режима вывода символов для видеомонитора (только MSDOS).

%vl - LOWVIDEO

%vh - HIGHVIDEO

%vn - NORMALVIDEO

%vx - смена режима BLINK/не BLINK

%vb# - новый цвет фона ('#' == 0,...,9,A,...F )

%vf# - новый цвет символов

СПЕЦИФИКАТОРЫ ТИПОВ

d,D - целочисленный тип int, unsigned int, long, insigned long. Буква D означает, что длина поля будет принята такой, чтобы поместить наибольшее число данного типа.

s - текстовая C-строка.

us - текстовая UNICODE-строка

e, g, f, re, rg, rf - текущий тип REAL.

Re, Rf, Rg - текущий REAL с выводом всех значащих цифр.

rre,rrg,rrf,Rre,Rrg,Rrf - принудительно тип REAL4...REAL18

rce, rcf, rcg - поле LemComplex.

Rce, Rcg, Rcf - поле типа LemComplex с выводом максимума знаков.

Rde, Rdf, Rdg, rde, rdf, rdg - поле DUPEL.

hc - печать строки символов 'c'. Длина последовательности равна установленной длине поля.

Hc - печать строки символов 'c', длина строки берется со стека.

x - печать последовательности из символа, который считывается со стека.

c - печать единичного символа.

uc - печать единичного UNICODE-символа.

b - логические bool.

t - табуляция до позиции field_len от начала текущей строки.

T - табуляция до позиции, снимаемой со стека.

Yc - вывод символов 'c' от текущей позиции до field_len.

M, m - установка специального режима полей:

Ml, ml - установка поля слева, число пробелов берется со стека (M) или из форматной строки (m)

Mw, mw - ширина страницы, считая и левое поле, число знакомест берется со стека (M) или из форматной строки (m)

Mn, mn - возврат к параметрам по умолчанию

*:lang(слово[,падеж]) - вывод слова, согласованного с числовым аргументом типа int, для указанного падежа. Используется словарь языка lang. Допустимые значения lang: 'ru' и 'en' без кавычек. Пример: %*:en(file) или %*:ru(файл,им). По умолчанию - именительный падеж. Для выполнения грамматических операций используется Micro_Solaris.

ЗНАЧЕНИЕ СПЕЦИАЛЬНЫХ ФЛАГОВ ...Fnm...

1. Для стандартных целочисленных.

Fz1 - если размер поля больше, чем получилось значимых цифр, то слева дописываются нули. Следует помнить, что по умолчанию, если не встретился спецификатор ...k[.l]..., то длина поля принимается максимальной для типа [unsigned][long]int, чтобы разместить число MAXINT, UINT_MAX и так далее. Если спецификатор ...k[.l]... дает размер поля больше, чем получается знакомест, то флаг Fz1 срабатывает.

2. Для комплексных.

Ff0 - вывод в виде '{re,im}' (режим по умолчанию).

Ff1 - вывод в виде '(re,im)'

Ff2 - вывод в виде 're im'

Ff3 - вывод в виде 're=x im=y'

3. Для bool.

Ff0 - 'true'/'false' (режим по умолчанию)

Ff1 - '.TRUE.'/'.FALSE.' (для ФОРТРАНовских программ)

Ff2 - '1'/'0'

4. Для типа DUPELxxx

Ff1 - вывод в виде { среднее +/- отклонение }

5. Прочие.

F$3 - формат вывода денежных сумм с разделением запятой по 3 цифры справа.

F$4 - аналогично предыдущему, но с разделением пробелом

Коды цветов

Очень важная фича класса OFormatter - поддержка расцвечивания выводимых символов. Эта мелочь выливается в большое удобство при создании консольных утилит - в качестве примера можно посмотреть на процесс работы YC - компилятора Словарей. Вывод в цвете поддерживается и на платформе Linux (через библиотеку ncurses). Пример такого использования OFormatter'а - компилятор YGRES:

colored_console

Изменение цвета выводимых символов осуществляется флагом %vfN, где N - один из символов:

0

BLACK

 

1

BLUE

 

2

GREEN

 

3

CYAN

 

4

RED

 

5

MAGENTA

 

6

BROWN

 

7

LIGHTGRAY

 

8

DARKGRAY

 

9

LIGHTBLUE

 

A

LIGHTGREEN

 

B

LIGHTCYAN

 

C

LIGHTRED

 

D

LIGHTMAGENTA

 

E

YELLOW

 

F

WHITE

 

Флаг %vn восстанавливает значение цвета по умолчанию (обычно светло-серый).

Кодовые страницы

Изначально планировалось, что класс OFormatter будет работать с 8-битовыми символами и потоками вывода. Со временем это ограничение стало неприемлемым, и были введены некоторые средства работы с кодовыми страницами.

Метод OFormatter::SetOutCP указывает, какую кодовую таблицу использовать для ВЫВОДИМЫХ в поток символов. Например, для окна консоли нужно указывать кодовую страницу 866 (если в системном реестре не изменены установки кодовой страницы):

mout.SetOutCP( &cp866 );

Метод OFormatter::SetLocalCP указывает, в какой кодовой странице поступают символы для форматирования. Допустим, что в программе текстовые сообщения по тексту сделаны в кодовой странице 1251 (т.е. стандартной для русской локализации MS Windows). Программа должна работать в Linux (так, как это делают большинство утилит Проекта). Если локаль в Linux установлена в кодовую страницу 847 (стандартная для Unix'ов), то необходимо инициализировать форматтер вывода так:

mout.SetLocalCP( &cp1251 );
mout.SetOutCP( &cp847 );

В приводимых примерах обозначения кодовых страниц cp866 и т.д. - это имена глобальных объектов, на которые можно ссылаться в любом месте программы. Подробное описание классов для кодовых страниц дано здесь.

Формирование utf-8, utf-16 файлов

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

Для решения задачи пишем такой фрагмент кода:

// открываем потоки
UTF8_Reader *utf8 = new UTF8_Reader( boost::shared_ptr<BaseStream>( new BinaryFile( Path("out.xml"), false, true ) ) );
stream = new OFormatter( utf8, true );
stream->SetOutCP( &cp1251 );
utf8->set_CodePage( &cp1251 );
 

// пишем в поток
 stream->printf( "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n<files>\n" );

/* ... */
stream->printf( "</files>\n" );

// закрываем потоки
delete stream;
 

Потоки, базовые и сложные текстовые, описаны здесь. Конструкция tagged_ptr<...>(...) - это создание управляемого указателя, который умеет сам удалять объект (либо не удаляет объект - если параметр это запрещает). Подробнее об управляемых указателях см. здесь.

При формировании unicode-файлов таким способом возникают некоторые накладные расходы. Это происходит по следующей причине. Итоговым результатом работы класса OFormatter является последовательность символов char. Если, допустим, печатается строка из символов wchar_t (флаг %us), то символы преобразуются в char с помощью установленной методом SetOutCP() кодовой страницы. Затем char-символы попадают на вход потока (производный от Char_Stream::Base_Reader класс). В нормальном режиме эти потоки получают wchar_t символы в методе ::wput(). Когда же вызывается метод ::put(), то char-символ с помощью установленной для Base_Reader'а коловой таблицы преобразуется в wchar_t.

Таким образом, происходит бесполезное двойное преобразование wchar_t - char - wchar_t.

Избежать накладных расходов можно использованием класса OUFormatter. Для вывода результатов он создает текстовый поток в кодировке utf-8, поэтому вышеприведенный фрагмент программы упростится до (показано начало):

// открываем потоки
stream = new OUFormatter( Path("out.xml") );
 

Создаваемый файл out.xml будет иметь кодировку utf-8.

Как происходит расцвечивание

Сам по себе класс OFormatter не занимается манипуляциями с атрибутами вывода символов на консоль для задания цветов. Вместо этого он использует несколько виртуальных методов у класса абстрактного потока:

SetForeGroundColor

SetBackGroundColor

SetNormalMode

Если физический поток ввода-вывода, реализующий абстрактный интерфейс, поддерживает управление цветом символов, то он перегружает эти методы, в противном случае эти методы ничего не делают. Для вывода на консоль используется поток TtyStream (см. заголовочный файл streams.h в исходниках библиотеки LEM).

Последние изменения 14.11.2005