Использование Spreadsheet::WriteExcel

Задача, есть результаты школьных олимпиад. Нужно разослать результаты по школам. Исходные данные лежат в файле в виде: 2#МОК#Федяков#Андрей#9#М#ГОР#ВОРОНЕЖ#УЛ ШЕНДРИКОВА 7#394086#0732 317825#0#0#0#0#0#0#0#0#0#0#0#0 2#МОК#Амшеникова#Наталия#8#М##ВОРОНЕЖ#УЛ ШЕНДРИКОВА 7#394086#0732 317821#0#0#0#2#0#3#5#0#3#5#0#18 2#УЧЕБНО ВОСПИТАТЕЛЬНЫЙ КОМПЛЕКС#Протасов#Виталий#8#М#ГОР#ВОРОНЕЖ#УЛ ГЕРОЕВ СИБИРЯКОВ 5#394051#80732 335836#7#0#7#0#7#0#2#0#7#7#0#37 1#МУНИЦИПАЛЬНЫЙ ЛИЦЕЙ#Головин#Алексей#8#М#ГОР#ВОРОНЕЖ#УЛ ЛИЗЮКОВА 81#394088#0732 137587#7#7#7#7#7#6#7#7#7#7#0#69 0#ЯЗ ГИМНАЗИЯ ИМ А В КОЛЬЦОВА#Корж#Дмитрий#11#Б##ВОРОНЕЖ# ВОЛОДАРСКОГО 41#394000#0732 552759#3#8#6#8#7#4#6#0#0#0#0#42 0#ЯЗ ГИМНАЗИЯ ИМ А В КОЛЬЦОВА#Ладная#Екатерина#11#Б##ВОРОНЕЖ# ВОЛОДАРСКОГО 41#394000#0732 552759#4#5#4#7#6#3#3#0#0#0#0#32 0#ЯЗ ГИМНАЗИЯ ИМ А В КОЛЬЦОВА#Кузнецова#Наталья#11#Б##ВОРОНЕЖ# ВОЛОДАРСКОГО 41#394000#0732 552759#4#5#4#6#6#4#3#0#0#0#0#32 0#ЯЗ ГИМНАЗИЯ ИМ А В КОЛЬЦОВА#Де-Жорж#Инна#11#Б##ВОРОНЕЖ# ВОЛОДАРСКОГО 41#394000#0732 552759#4#5#4#6#6#4#3#0#0#0#0#32 0#ЯЗ ГИМНАЗИЯ ИМ А В КОЛЬЦОВА#Загонова#Виктория#11#Б##ВОРОНЕЖ# ВОЛОДАРСКОГО 41#394000#0732 552759#4#5#4#8#5#1#3#0#0#0#0#30 0#ГИМНАЗИЯ ИМ А В КОЛЬЦОВА#Гудков#Илья#11#Б#ГОР#ВОРОНЕЖ#УЛ ВОЛОДАРСКОГО 41#394000#0732 552759#4#4#4#6#3#2#3#0#0#0#0#26 2#МОК#Вдовина#Вера#11#Б##ВОРОНЕЖ#УЛ ШЕНДРИКОВА 7#394086#80732 317825#2#6#5#5#2#2#2#0#0#0#0#24 72#ШКОЛА#Холявка#Марина#11#Б##ВОРОНЕЖ# ЮЖНОМОРАВСКАЯ 18#394062#0732 331084#5#6#8#7#9#6#9#0#0#0#0#50 4#СРЕДНЯЯ ШКОЛА#Лукина#Дарья#11#Б#ГОР#ВОРОНЕЖ#БУЛЬВАР ПИОНЕРОВ 14#394038#0732 336762#3#6#6#2#3#3#2#0#0#0#0#25 4#СРЕДНЯЯ ШКОЛА#Кварацхелия#Кристина#11#Б#ГОР#ВОРОНЕЖ#БУЛЬВАР ПИОНЕРОВ 14#394038#0732 336730#3#5#6#3#3#3#0#0#0#0#0#23 ... и т.д. Сначала это, правда, был файл *.dbf, но его при помощи программы #!/usr/bin/perl qx[dbfdump --fs="\x18" --rs="\x19" olimp.dbf >one.txt]; сделали текстовым, потом perl -i -n -p -e 's!\x18!#!; s!\x19!\n!;' one.txt получили исходный. Нужно сделать рассылку по школам, т.е. отсортировать по городам, по названиям школ и в каждую поместить по пользователю. Выходные данные должны быть в формате excel, т.к. для каждого конверта нужны полный адрес школы и список результатов каждого учащегося. Причем, excel позволяет отдавать на печать файлы постранично, т.е. для каждой школы отдельная страничка и потом удобно все это раскладывать по конвертам, т.е. нужно в файле excel поставить разрывы страниц. Данную задачу реализует следующий скрипт: #!/usr/bin/perl use Spreadsheet::WriteExcel; open F, "<one.txt"; @mass=<F>; close F; @res1=grep{s!^(.*?#.*?#)(.*?#.*?#.*?#.*?#)(.*?#.*?#)!$3$1$2!} @mass; @res = grep{!$_{$_}++} map{/^(.*?#.*?#).*?#.*?#/} @res1; for $gr(@res){ for $line(@res1){push @{$hash{$gr}{$1}}, $2 if $line=~m!$gr(.*?#.*?#)(.*)$!} } my $workbook = Spreadsheet::WriteExcel->new("olimp2.xls"); my $sheet = $workbook->addworksheet("all children"); my $format = $workbook->addformat(); $format ->set_text_wrap(); $format->set_bold(); $format->set_size(11); $format->set_color('blue'); $format->set_align('center'); $sheet->set_column(1, 3, 70); $sheet->set_row(0,30); $sheet->activate(); print "end build hash\n"; print "write file...\n"; for $a(sort keys %hash){ $m=$a; $m=~s!#! !ig; for $key(sort keys %{$hash{$a}}){ $pb++; $u=$key; $u=~s!#! !ig; $i++; my $from = join " " => @{[split /#/ => ${$hash{$a}{$key}}[0]]}[4..6]; $sheet->write($i, 0, "$m $u $from", $format) if $pb >= 2000; for my $test(sort @{$hash{$a}{$key}}){ $i++; $h++; my $name = join " " => @{[split /#/ => $test]}[0 .. 3]; my $nums = join " " => @{[split /#/ => $test]}[7 .. 18]; $sheet->write($i, 0, "$name $nums") if $pb >= 2000; } print "$pb\n" if $pb % 1000 == 0; $sheet->set_h_pagebreaks($i+1) if $pb >= 2000; } } print "$m $u $from\n"; print "$h - done\n"; Разберем его работу. Скрипт использует хеши хешей массивов. Строчка @res1=grep{s!^(.*?#.*?#)(.*?#.*?#.*?#.*?#)(.*?#.*?#)!$3$1$2!} @mass; заполняет массив будущими ключи хеша по названию города и улицы, т.к. в городе Саратов вполне может встретится воронежская или ленинградская улица, что смешает результаты. Строчка @res = grep{!$_{$_}++} map{/^(.*?#.*?#).*?#.*?#/} @res1; убирает повторяющиеся элементы для вложенного хеша, т.е. выделяет названия адресов школ. Цикл for $gr(@res){ for $line(@res1){push @{$hash{$gr}{$1}}, $2 if $line=~m!$gr(.*?#.*?#)(.*)$!} } заполняет хеш хешей массивов. Далее требуется чтение man Spreadsheet::WriteExcel и начинаем вывод их хеша и запись файла excel. Строчка my $from = join " " => @{[split /#/ => ${$hash{$a}{$key}}[0]]}[4..6]; расшифровывается как взять нулевой элемент массива ${$hash{$a}{$key}}, потом разрезать его по строчкам в массив по разделителю #: [split /#/ => ${$hash{$a}{$key}}[0]]. Далее взять от массива срез, превратить его в строку, которая будет являться адресом. Одно "но", excel не позволяет ставить более 1000 pagebreaks для каждого открытого файла, и поэтому приходится придумывать различные условия, вроде постраничного вывода. Т.к. надо было сделать быстро, то дабы не писать постраничный вывод обошелся условием if $pb >= 1000 ... etc....

Т.е. следите за обновлениями www.cpan.org! :)