Наши партнеры

UnixForum






Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

Ошибка базы данных: Table 'a111530_forumnew.rlf1_users' doesn't exist
На главную -> MyLDP -> Тематический каталог -> СУБД для Linux

Создание Excel-файлов в Perl

Оригинал: Generating Native Excel Files in Perl
Автор: Mike Diehl
Дата: 11 августа 2008
Перевод: Александр Тарасов aka oioki
Дата перевода: 9 декабря 2008

За много лет мне приходилось создавать тысячи отчетов для клиентов и коллег. Обычно я пользовался веб-программой, генерирующей отчеты в HTML-формате или в формате, где разделителем служит запятая (CSV). HTML-формат намного симпатичнее и обычно этого пользователям хватает. Данные в формате CSV можно с легкостью импортировать в табличный процессор Open Office Calc или Excel. С помощью этих программ пользователи выполняют форматирование исходных данных и затем отправляют окончательный отчет куда нужно. Причем зачастую те, кто должны периодически отправлять отчеты одного и того же типа, неоднократно выполняют это форматирование, по сути каждый раз выполняя вручную одну и ту же работу. Они рады, что у них есть данные и не сильно беспокоятся о том, насколько красиво они представлены. Но есть решение лучше.

Perl-модуль Spreadsheet::WriteExcel позволяет генерировать данные, форматировать их и выводить в родной формат табличного процессора Excel. Мне остается лишь дать пользователям гиперссылку на отчеты.

Рассмотрим простой CGI-скрипт, использующий модуль Spreadsheet::WriteExcel. Код, представленный в этой статье, основан на программе, написанной мной некоторое время назад для пользователей. Он не такой красивый (или корректный) какой должен быть. Это лишь пример того, чего можно достичь с помощью модуля Spreadsheet::WriteExcel. Однако уверяю вас, результаты работы даже такой программы впечатляют!

Вот основа скрипта:

#====================================================
#!/usr/bin/perl

use DBI;
use CGI;
use Spreadsheet::WriteExcel;

$dbh = DBI->connect("dbi:Pg:dbname=test;host=db.example.com", "postgres", "passwd") ||
die "Can't connect to database.\n";

$cgi = new CGI;

print $cgi->header("application/excel");

#====================================================

В этом куске кода мы загружаем DBI-модуль - впоследствии нам придется запрашивать данные у СУБД PostgreSQL. Затем мы загружаем CGI-модуль, упрощающий написание CGI-скриптов, а мы ведь любим писать легко, ведь так? Затем мы подключаемся к базе данных и получаем ее handle-идентификатор для дальнейшего использования. Аналогично, получаем идентификатор CGI, для дальнейшей работы с модулем CGI.

Эта программа решает единственную задачу - возвращает файл в формате Excel, поэтому MIME-тип задается жестко. Если ваш скрипт содержит форму извлечения данных, или выпадающий список, в котором нужно выбрать отчет, тогда вызов header() стоит переместить в более подходящее для этого место.

Пора генерировать наш отчет.

#====================================================
my $workbook = Spreadsheet::WriteExcel->new("-");

my $worksheet = $workbook->add_worksheet("Cover Sheet");

my $bold = $workbook->add_format();
$bold->set_bold();

$worksheet->write(5, 0, "Division Number:", $bold);
$worksheet->write(5, 1, $division_number);

#====================================================

В этой части мы создаем идентификатор типа Spreadsheet::WriteExcel и сохраняем его в переменной $workbook. Здесь мы передаем конструктору объекта не имя файла, а просто "-". Это означает, что результирующая электронная таблица будет выведена не в файл, а на стандартный вывод STDOUT. Наш отчет будет содержать простой титульный лист, поэтому давайте сейчас добавим лист. Мне нужно вывести надпись "Division Number" полужирным шрифтом, для этого нужно проделать несколько шагов. Во-первых, создадим новый объект типа "формат", с помощью метода add_format(). Затем этому формату можно приписывать различные атрибуты; в нашем случае нужно сделать его полужирным. Мы могли бы сделать буквы красными на зеленом фоне, наклонный шрифт и выравнивание справа. Но зачем? В нашем случае мы просто хотим выделить надпись, поэтому мы всего лишь сделали ее полужирной.

Наконец, мы помещаем данные в наш лист, с помощью метода write(). Сначала создаем полужирную метку "Division Number", в первом столбце шестой строки. Обратите внимание, что в Excel строки и столбцы нумеруются с единицы, а в Spreadsheet::WriteExcel - с нуля. Еще одно замечание - порядок индексации - "строка, столбец", а не "горизонтальный индекс, вертикальный индекс". Когда я впервые воспользовался Spreadsheet::WriteExcel, я этого не понял, и удивлялся, почему мои таблицы выглядели столь забавно.

Переменная $division_number не пуста, она содержит целочисленное значение, которое должно быть где-либо определено.

Теперь добавим второй лист и разместим на нем какие-либо настоящие данные.

#====================================================
$worksheet = $workbook->add_worksheet("Customer Details");

my $current = $workbook->add_format();
my $overdue = $workbook->add_format();

$current->set_color('green');
$current->set_num_format('$0.00');

$overdue->set_color('red');
$overdue->set_num_format('$0.00');

$col=0;
foreach $i ("Customer Name", "Customer Number","Phone Number","Amount Due") {
	$worksheet->write(0, $col++, $i, $bold);
}

$sth = $dbh->prepare("select name, number, phone, due from customers where
division=$division_number order by name");
$sth->execute();

my $row=1;
while ($a = $sth->fetchrow_hashref()) {
	$worksheet->write($row,0, $a->{name}, $bold);
	$worksheet->write($row,1, $a->{number});
	$worksheet->write($row,1, $a->{phone});

	if ($a->{due} > 1000) {
		$worksheet->write($row,1, $a->{due},$overdue);
	} else {
		$worksheet->write($row,1, $a->{due}, $current);
}

$row++;
}
#====================================================

Теперь код выглядит действительно интересно. После создания нового листа, следующим шагом мы создаем еще два формата. Один формат будет красным - для клиентов-должников, и зеленым - для обычных клиентов. Затем, денежные суммы мы хотим отображать со знаком доллара, поэтому устанавливаем соответствующий формат ячейки.

Далее следует шапка таблицы. Я ленив и не люблю добавлять столбцы вручную, поэтому шапка таблицы печатается в цикле for. Кроме того, добавить новый столбец не представляет никаких сложностей - нужно лишь добавить его в список цикла for.

Теперь создаем запрос к базе данных. В данном случае мы запрашиваем список клиентов - имя, номер, номер телефона и величину долга.

Имя выводим полужирным шрифтом, далее следует номер клиента и телефонный номер, все это обычным шрифтом.

Последний столбец немного интереснее. В последнем столбце мы проверяем, задолжал ли клиент нам более чем $1000. Если это так, мы раскрашиваем его ячейку в красный цвет, тем самым выделяя его. Иначе раскрашиваем в зеленый формат $current.

После того, как мы вывели всю строку, переходим на следующую, с помощью оператора $row++ и забираем следующую запись из нашего набора данных.

Мы почти закончили.

#====================================================
$dbh->finish();
$workbook->close();
exit;
#====================================================

Всегда нужно убираться за собой, а именно завершать работу с идентификатором БД, вызывая метод finish(). Закрытие идентификатора таблицы методом close() приводит к очистке буферов и выдаче готовой таблицы на стандартный вывод STDOUT.

Конечно, если бы мы писали генератор CSV-файла, то программа уместилась бы в 10 строк. Но тогда бы пользователю пришлось совершать дополнительную работу, а именно импортирование данных и форматирование получившейся таблицы. В то же время написанная нами только что программа проста, а результаты ее работы намного симпатичнее. На рисунке показан результат работы скрипта, после того как я вручную изменил ширину некоторых столбцов. Модуль Spreadsheet::WriteExcel позволяет выполнять и другие, более сложные варианты форматирования, например, помещение формул в ячейку. Модуль настолько прост в использовании, а отзывы пользователей положительны, что я даже не хочу больше заниматься CSV-отчетами.



Комментарии