При практической эксплуатации информационных систем уровня предприятия, особенно при недостаточно развитой системе подготовки отчетов, часто бывает необходимо заполнить разного рода бланки (например, заявления, справки, заключения и т.д.) или подготовить отчеты для распечатки на лазерном принтере, примерно в таком виде:
Пример rtf-бланка
Заголовок документа: PARAM1
Строка 1 | Значение | PARAM2 |
Строка 2 | Значение | PARAM3 |
Подпись под документом: PARAM4
Рис.1 Пример бланка в виде rtf-файла, переменные описаны в виде полей типа “QUOTE” – PARAM1,PARAM2,PARAM3,PARAM4
При заполнении бланка данными, переменные PARAM1...4 должны будут замениться на свои значения.
При этом форма бланка обычно остается постоянной и неизменной, а конкретные поля в бланке необходимо “подгружать” из таблиц баз данных, файлов с данными и т.д. В терминах языка Perl – значениями хеша, ключи которого соответствуют названиям переменных:
(PARAM1=>”ПАРАМЕТР1”, PARAM1=>”ПАРАМЕТР2”, PARAM1=>”ПАРАМЕТР3”,
PARAM1=>”ПАРАМЕТР4”)
В результате, готовый к печати на лазерном принтере бланк, будет иметь примерно такой вид:
Пример rtf-бланка
Заголовок документа: ПАРАМЕТР1
Строка 1 | Значение | ПАРАМЕТР2 |
Строка 2 | Значение | ПАРАМЕТР3 |
Подпись под документом: ПАРАМЕТР4
Рис.2 Результат заполнения rtf-бланка (рис.1) заменой полей типа “QUOTE” на значения соответствующих ключей хеша.
При этом возникает необходимость выполнить несколько трудносовместимых требований -разработка и исправление бланка должно быть доступно конечному пользователю (бухгалтеру, сотруднику отдела кадров, менеджеру и т.д.) и позволять произвольно расставлять поля для слияния данных, а подготовка процедуры извлечения данных — программисту или сотруднику службы поддержки, то есть, необходимо отделить представление данных от процедуры извлечения данных.
Можно, разумеется, сказать, что подобная технология слияния данных давно используется в MS Word, например, но она имеет серьезный недостаток: процедура слияния довольно сложна для неподготовленного пользователя (необходимо подготовить файл с данными, выбрать бланк, запустить процесс слияния, разобраться с результатами).
Другой вариант – использование бланков в форме XML-файлов и заполнение их в соответствии с правилами подстановки, описанными в XSLT-таблицах,- имеет тот недостаток, что неподготовленному пользователю, привыкшему работать с MS Word будет трудно создавать и редактировать бланки XML-файлов. Какой-либо общераспространенный инструмент для работы с XML, сравнимый по популярности с MS Word трудно найти.
Выходом, разработанным и апробированным уже в течение ряда лет при работе в крупной банковской информационной системе стал вариант автоматической подстановки данных в rtf-бланки отчетов с использованием простого скрипта (в данном случае использовался perl, но возможно использование любого языка, поддерживающего обработку регулярных выражений), осуществляющего поиск в текстовом rtf-документе описателей полей типа “QUOTE” и замену символических названий этих полей на значение переменной с аналогичным именем, извлеченной из таблицы БД, файла данных (в терминах perl – из хеша по ключу, соответствующему символическому названию поля).
Пример скрипта c комментариями, выполняющего подобную подстановку, приведен в конце постинга.
Скорее всего, даже краткого пояснения принципа работы скрипта не требуется (готовый rtf-бланк с выполненными подстановками значений переменных выводится во временный файл temp.rtf, подготовленный к отправке на принтер), единственная представляющая в нем интерес часть – регулярное выражение:
$rtf =~ s/{\fieldb[^{}]*{\*\fldinst.*?{\fldrslts*{([^}]+s)?s*([^}]+s) ?s*([^}]+)}s*((?:{[^{}]+}s*)*)}s*}/dosubst($1,$2,$3,$p,$&)/gesx;
осуществляющее поиск в rtf-файле описателей полей типа “QUOTE” и замену их соответствующим значением из хеша %p, возвращаемым функцией dosubst. Работоспособность этого регулярного выражения проверена на rtf-файлах, подготавливаемых всеми пакетами MS Office – от Office 95 до Office 2010.
Конечно, данный способ имеет некоторые недостатки, например, довольно трудно осуществлять вывод в rtf-бланк табличных значений, особенно для длинных таблиц, (их, например, можно выводить сразу в готовом виде, строя в perl-скрипте), но имеет и серьезные плюсы:
— разработку бланка и расстановку в нем полей для размещения данных может выполнять обычный пользователь, знакомый с редактором Word (умеющий вставлять в текст поля типа “QUOTE”), сообщающий затем список названий полей данных и что в них желательно помещать при подстановке программисту или специалисту службы поддержки;
— разработку скрипта, выгружающего данные, осуществляет программист, а в дальнейшем, при необходимости косметических изменений бланка (шрифт, отступы, логотип и т.д.), программиста привлекать необходимости нет, с этой работой легко справляется или служба поддержки, или сам пользователь – автор бланка.
Таким образом, легко выполняется разделение формы представления данных, за которую отвечает пользователь или служба поддержки, от собственно данных, процедуру извлечения которых разрабатывает программист.
#!/usr/bin/perl
# filling rtf-blank
# Dim Kobzev, Andrew Sapognikov
# 2004
$==1000;
#-------------------------filling template file-------------------------------
sub dosubst {
my ($pfx, $key, $xtrakey, $parm, $str) = @_;
if (defined $xtrakey) {
$xtrakey =~ s/[{}]//g;
$xtrakey =~ s/\w+//g;
$xtrakey =~ s/s//g;
$key .= $xtrakey;
}
defined ($parm->{$key}) or return "{$pfx}";
my $val = $parm->{$key};
$val =~ s/([{}\])/\$1/g;
$val =~ s/n/\line/g;
$val =~ s/r//g;
return "{$pfx$val}";
}
sub template ($$) { #filling $filename with hash $p
my ($filename,$p)=@_;
my $rtf;
local $/, *F;
open (F,"< $filename") or die "Cannot open $filename";
$rtf=<F>; close(F);
#set win-1251 codepage
$rtf =~ s/\f(5|6)\fmodern\fcharset0/\f$1\fmodern\fcharset144/;
$rtf =~ s/{\fieldb[^{}]*{\*\fldinst.*?{\fldrslts*{([^}]+s)
?s*([^}]+)}s*((?:{[^{}]+}s*)*)}s*}/dosubst($1,$2,$3,$p,$&)/gesx;
return $rtf;
};
#-------------------------main--------------------------------------------------
%p=('PARAM1'=>"ПАРАМЕТР1",'PARAM2'=>"ПАРАМЕТР2",
'PARAM3'=>"ПАРАМЕТР3",'PARAM4'=>"ПАРАМЕТР4");
my $tfil='temp.rtf';
open *FILE, "> $tfil"
or die "Cannot open temporary file $tfil";
print FILE template("blank.rtf",%p);
close *FILE;
#---------------------------------end main-------------------------------------
Автор: Kodim