Работа над проектом Lindocs. Скрипты конверта из текущего состояния
Работа над проектом Lindocs. Скрипты конверта из текущего состояния
Создаем список обрабатываемых документов:
$ ls -1 *.html > htmllist
Удаляем те документы, которые не несут данных:
begin.html
default.html
index.html
Удалим "не-юниксовые" окончания строк в html-файлах:
$ ls -1 *.html | xargs dos2unix
Каждый файл выглядит примерно следующим образом:
<!-- тут стандартное для всех начало -->
<strong> Теоретические основы</strong><BR>
<BR>
<a href="Linux/Docs/vodol.html">В. Водолазкий "Путь к LINUX"</a><br>
<br>
<a href="Linux/other/html/acquaintance_part_01.htm">Знакомство с Linux.</a>
<a href="Linux/Sites/www.kuzbass.ru/docs/unixprogenv/ch1.txt">1</a>
<a href="Linux/other/html/acquaintance_part_02.htm">2</a>
<a href="Linux/other/html/acquaintance_part_23.htm">23</a><br>
В.А.Костромин. 14 дней жизни одной операционной системы.<br>
<a href="Linux/Docs/general/day_1-6.htm">День 1-6</a><br>
<a href="Linux/Sites/MyComp/224_02.htm">Висит? Снимем!</a><br>
<!-- тут стандартный для всех конец -->
Начнем писать скрипт по шагам:
Шаг первый. Вычленяем информацию из одной строки
Напишем отдeльную функцию извлекающую ссылку и ее описание из
передаваемой строки.
sub extract_data {
my($l_str) = @_[0];
my $l_link = $l_str;
my $l_desc = $l_str;
# Выделяем ссылку из строки
$l_link =~ s/(?i)(.*)\<a\shref\=\"(.*)\"\>(.*)/\2/;
# Выделяем описание из строки
$l_desc =~ s/(?i)(.*)\<a\shref\=\"(.*)\"\>(.*)\<\/a\>(.*)/\3/;
return ($l_link, $l_desc);
}
Напишем отдeльную функцию извлекающую описание темы материала,
состоящего из нескольких частей:
sub extract_topic {
my($l_str) = @_[0];
# Выделяем описание из строки
my $l_desc = $l_str;
$l_desc =~ s/(?i)^(.*)\<BR\>(.*)/\1/;
return $l_desc;
}
Напишем отдeльную функцию извлекающую описание для всего файла, т.е.
для индекса:
sub extract_index {
my($l_str) = @_[0];
my $l_desc = $l_str;
# Выделяем описание из строки
$l_desc =~ s/(?i)^\<strong\>(\ \;){4}(.*)\<\/strong\>(.*)/\2/;
return $l_desc;
}
Шаг второй. Определение типа строки
Необходимо определять вид(тип) строки, для того чтобы определять
какую информацию из нее нужно извлекать и какой xml-блок генерировать
после. Напишем функцию, которая определяла бы тип строки, выбирая
эту строку по номеру из глобального массива всех строк, входящих в файл.
sub def_str_type {
my ($l_num) = @_[0];
my $l_res = 0;
# Строка, которая выглядит как обычная
if ( @f_array[$l_num] =~ /(?i)^\<a\shref.*\<br\>/ ) {
# Надо проверить следующие за ней
if ( def_str_type($l_num+1) == 31 ) {
# Строка - начало материала из нескольких частей с одной темой
# так как за ней соответствующая строка
$l_res = 30;
}
else {
# Все таки обычная строка
$l_res = 1;
}
}
# Строка - начало материала из нескольких частей
elsif ( @f_array[$l_num] =~ /(?i)^\<a\shref/ ) {
$l_res = 2;
}
# Строка - продолжение и конец материала из нескольких частей
elsif ( @f_array[$l_num] =~ /(?i)^\s+.*\<a\shref/ ) {
$l_res = 21;
}
# Строка - продолжение и конец материала из нескольких частей с одной
темой
elsif ( @f_array[$l_num] =~ /(?i)^(\ \;){2,}.*\<a\shref.*\<br\>/
) {
$l_res = 31;
}
# Строка выглядит как не несущая в себе данных, но за ней продолжение
материала
# состоящего из нескольких частей, значит, данная строка начало этого
материала
elsif ( ( $l_num+1 < $#f_array ) && ( def_str_type($l_num+1) == 31 )
) {
$l_res = 3;
}
return $l_res;
}
Видно, что внутри функции происходит ее рекурсивный вызов...
Шаг третий. Генерация xml-блоков
Процедура, которая учитывает переданные ей параметры и в зависимости
от них генерирует xml-блок для ссылки:
sub generate_record {
my ($l_type, $l_id, $l_part, $l_link, $l_desc, $l_index) = @_;
# Генерируем xml-блок
my $l_res = "";
$l_res = $l_res." <element";
length($l_type) != 0 ? $l_res = $l_res." type=\"topic\"" : $l_res = $l_res;
length($l_id) != 0 ? $l_res = $l_res." id=\"".$l_id."\"" : $l_res = $l_res;
length($l_part) != 0 ? $l_res = $l_res." part=\"".$l_part."\"" : $l_res
= $l_res;
$l_res = $l_res.">\n";
length($l_link) != 0 ? $l_res = $l_res." <link type=\"rel\">".$l_link."</link>\n"
: $l_res = $l_res;
$l_res = $l_res." <desc>".$l_desc."</desc>\n";
$l_res = $l_res." <author></author>\n";
$l_res = $l_res." <index>".$l_index."</index>\n";
$l_res = $l_res." </element>\n";
return $l_res;
}
Процедура, которая учитывает переданные ей параметры и в зависимости
от них генерирует xml-блок для индекса:
sub generate_index {
my ($l_index, $l_desc) = @_;
# Генерируем xml-блок
my $l_res = "";
$l_res = $l_res." <index>\n";
$l_res = $l_res." <value>".$l_index."</value>\n";
$l_res = $l_res." <parent></parent>\n";
$l_res = $l_res." <desc>".$l_desc."</desc>\n";
$l_res = $l_res." </index>\n";
return $l_res;
}
Шаг четвертый. Работа с содержимым html-файла
Итак, нужно последовательно перебирать все строки файла,
определять их тип, извлекать из нее нужные данные и генерировать
xml-блоки. Так как при генерации блока для ссылок, обозначающих
материал состоящий из нескольких частей, необходим уникальный
идентификатор, то его генерировать будем с помощью функции md5_base64
из модуля Digest::MD5.
while ( $count < $#f_array ) {
# Выбираем строки по одной
$next_s = @f_array[$count];
# Подошли к концу файла
if ( $is_content && ( ( $cur_s =~ /(?i)^\<\/BODY/ ) && ( $next_s =~ /(?i)^\<\/HTML/
) ) ) {
$is_content = 0;
}
if ( $is_content ) {
# В зависимости от типа строки генерируем необходимый элемент
$str_type = def_str_type($count-1);
if ( $str_type == 1 ) {
$xml_block = $xml_block.generate_record("", "", "", extract_data($cur_s),
$cur_index);
}
elsif ( $str_type == 2 ) {
$id_in = md5_base64($cur_s);
$part_in = 0;
$xml_block = $xml_block.generate_record("", $id_in, $part_in,
extract_data($cur_s), $cur_index);
}
elsif ( $str_type == 21 ) {
$part_in += 1;
$xml_block = $xml_block.generate_record("", $id_in, $part_in,
extract_data($cur_s), $cur_index);
}
elsif ( $str_type == 3 ) {
$id_in = md5_base64($cur_s);
$xml_block = $xml_block.generate_record(1, $id_in, "", "", extract_topic($cur_s),
$cur_index);
}
elsif ( $str_type == 30 ) {
$id_in = md5_base64($cur_s);
($var, $topic) = extract_data($cur_s);
$xml_block = $xml_block.generate_record(1, $id_in, "", "", $topic,
$cur_index);
$xml_block = $xml_block.generate_record("", $id_in, "", extract_data($cur_s),
$cur_index);
}
elsif ( $str_type == 31 ) {
$xml_block = $xml_block.generate_record("", $id_in, "", extract_data($cur_s),
$cur_index);
}
else {
print $cur_s."\n";
}
}
# Сейчас начнутся ссылки на материал
if ( ! $is_content && ( ( $cur_s =~ /(?i)^\<BR/ ) && ( $prev_s =~ /(?i)^\<strong/
) ) ) {
$is_content = 1;
$index_block = generate_index($cur_index, extract_index($prev_s));
}
$prev_s = $cur_s;
$cur_s = $next_s;
$count += 1;
}
Шаг пятый - заключительный. Сварка и спайка
После сварки и спайки получился следующий скрипт:
begin genrate.pl #!/usr/bin/perl
# Импортируем функцию из модуля, необходимую для генерации уникальных
# идентификаторов
use Digest::MD5 qw(md5_base64);
# Считываем параметры переданные из командной строки
# Имя файла с листингом обрабатываемых файлов
my $f_list = @ARGV[0];
# Имя файла куда помещаются результаты
my $f_out = @ARGV[1];
# Начальные значения рабочих переменных
# xml-блок, где накапливаются индексы
my $xml_index = " <indexes>\n";
# xml-блок, где накапливаются ссылки
my $xml_content = " <content>\n";
# Переменные, в которых находятся текущий индексный блок и текущий блок
# ссылок
my $t_xi = "";
my $t_xc = "";
##
# Функция: convert_one
# Входные параметры: $f_name: строковая величина
# Выходные данные: $index_block: строковая величина
# $xml_block: строковая величина
# Описание: Выполняет преобразование из html-файла, имя
# которого передано во входном параметре, в xml-блок по определенным
# правилам. В итоге получается два xml-блока: для индексной секции и
# секции ссылок. Эти блоки возвращаются как результат функции
sub convert_one {
# Имя файла, с которым работаем
my ($f_name) = $_[0];
# Список строк, входящих в файл
local @f_array = ();
# Заполняем список строк
open(F_IN, $f_name) || die "cannot open $f_name for reading: $!";
while (<F_IN>) {
chomp;
push(@f_array, $_);
}
# Закрываем файл
close(F_IN) || die "can't close $f_name: $!";
# Текущий индекс для документа
my $cur_index = substr($f_name, 0, length($f_name)-5);
# Начальное значение, обрабатываемой строки
my $cur_s = "";
# Строки вокруг обрабатываемой
my $prev_s = "";
my $next_s = "";
# Флаг наличия содержания
my $is_content = 0;
# Первоначальное значение счетчика строк
my $count = 0;
# Переменные, куда будет помещаться xml-блок, возращаемый как
# результат
my $index_block = "";
my $xml_block = "";
##
# Функция: generate_record
# Входные параметры: $l_type: строковая величина
# $l_id: строковая величина
# $l_part: строковая величина
# $l_link: строковая величина
# $l_desc: строковая величина
# $l_index: строковая величина
# Выходные данные: $l_res: строковая величина
# Описание: Генерирует xml-запись, атрибутами и полями которой
# являются переданные функции параметры. Сгенерированная запись
# помещается в переменную, которую функция возвращает как результат
sub generate_record {
my ($l_type, $l_id, $l_part, $l_link, $l_desc, $l_index) = @_;
# Генерируем xml-блок
my $l_res = "";
$l_res = $l_res." <element";
length($l_type) != 0 ? $l_res = $l_res." type=\"topic\"" : $l_res
= $l_res;
length($l_id) != 0 ? $l_res = $l_res." id=\"".$l_id."\"" : $l_res
= $l_res;
length($l_part) != 0 ? $l_res = $l_res." part=\"".$l_part."\"" :
$l_res = $l_res;
$l_res = $l_res.">\n";
length($l_link) != 0 ? $l_res = $l_res." <link type=\"rel\">".$l_link."</link>\n"
: $l_res = $l_res;
$l_res = $l_res." <desc>".$l_desc."</desc>\n";
$l_res = $l_res." <author></author>\n";
$l_res = $l_res." <index>".$l_index."</index>\n";
$l_res = $l_res." </element>\n";
return $l_res;
}
##
# Функция: generate_index
# Входные параметры: $l_index: строковая величина
# $l_desc: строковая величина
# Выходные данные: $l_res: строковая величина
# Описание: Генерирует xml-запись, атрибутами и полями которой
# являются переданные функции параметры. Сгенерированная запись
# помещается в переменную, которую функция возвращает как результат
sub generate_index {
my ($l_index, $l_desc) = @_;
# Генерируем xml-блок
my $l_res = "";
$l_res = $l_res." <index>\n";
$l_res = $l_res." <value>".$l_index."</value>\n";
$l_res = $l_res." <parent></parent>\n";
$l_res = $l_res." <desc>".$l_desc."</desc>\n";
$l_res = $l_res." </index>\n";
return $l_res;
}
##
# Функция: extract_data
# Входные параметры: $l_str: строковая величина
# Выходные данные: $l_link: строковая величина
# $l_desc: строковая величина
# Описание: Извлекает из входной строки ссылку и описание
# материала, которые потом возвращает как результат
sub extract_data {
my($l_str) = @_[0];
my $l_link = $l_str;
my $l_desc = $l_str;
# Выделяем ссылку из строки
$l_link =~ s/(?i)(.*)\<a\shref\=\"(.*)\"\>(.*)/\2/;
# Выделяем описание из строки
$l_desc =~ s/(?i)(.*)\<a\shref\=\"(.*)\"\>(.*)\<\/a\>(.*)/\3/;
return ($l_link, $l_desc);
}
##
# Функция: extract_topic
# Входные параметры: $l_str: строковая величина
# Выходные данные: $l_desc: строковая величина
# Описание: Извлекает из входной строки описание материала,
# которое является описанием для целой темы
sub extract_topic {
my($l_str) = @_[0];
# Выделяем описание из строки
my $l_desc = $l_str;
$l_desc =~ s/(?i)^(.*)\<BR\>(.*)/\1/;
return $l_desc;
}
##
# Функция: extract_index
# Входные параметры: $l_str: строковая величина
# Выходные данные: $l_desc: строковая величина
# Описание: Извлекает из входной строки описание, которое
# является описанием индекса
sub extract_index {
my($l_str) = @_[0];
my $l_desc = $l_str;
# Выделяем описание из строки
$l_desc =~ s/(?i)^\<strong\>(\ \;){4}(.*)\<\/strong\>(.*)/\2/;
return $l_desc;
}
##
# Функция: def_str_type
# Входные параметры: $l_num: целое число
# Выходные данные: $l_res: строковая величина
# Описание: Функция работает с глобальным массивом @f_array,
# определяя тип строк, которые в него входят, номер строки - $l_num
# Тип, определенный у строки, возращается как результат. В функции
# происходит рекурсивный вызов самой себя.
# Возвращаемые типы:
# 0 - строка не несущая информации
# 1 - обычная ссылка
# 2 - ссылка - начало материала из нескольких частей
# 21 - ссылка - продолжение или конец материала из нескольких частей
# 3 - строка - начало материала из нескольких частей с одной темой
# 30 - ссылка - начало материала из нескольких частей с одной темой
# 31 - ссылка - продолжение или конец материала из нескольких частей
# с одной темой
sub def_str_type {
my ($l_num) = @_[0];
my $l_res = 0;
# Строка, которая выглядит как обычная
if ( @f_array[$l_num] =~ /(?i)^\<a\shref.*\<br\>/ ) {
# Надо проверить следующие за ней
if ( def_str_type($l_num+1) == 31 ) {
# Строка - начало материала из нескольких частей с одной
темой
# так как за ней соответствующая строка
$l_res = 30;
}
else {
# Все таки обычная строка
$l_res = 1;
}
}
# Строка - начало материала из нескольких частей
elsif ( @f_array[$l_num] =~ /(?i)^\<a\shref/ ) {
$l_res = 2;
}
# Строка - продолжение и конец материала из нескольких частей
elsif ( @f_array[$l_num] =~ /(?i)^\s+.*\<a\shref/ ) {
$l_res = 21;
}
# Строка - продолжение и конец материала из нескольких частей с одной
темой
elsif ( @f_array[$l_num] =~ /(?i)^(\ \;){2,}.*\<a\shref.*\<br\>/
) {
$l_res = 31;
}
# Строка выглядит как не несущая в себе данных, но за ней продолжение
материала
# состоящего из нескольких частей, значит, данная строка начало этого
материала
elsif ( ( $l_num+1 < $#f_array ) && ( def_str_type($l_num+1) == 31
) ) {
$l_res = 3;
}
return $l_res;
}
# Цикл выборки строк из файла
while ( $count < $#f_array ) {
# Выбираем строки по одной
$next_s = @f_array[$count];
# Подошли к концу файла
if ( $is_content && ( ( $cur_s =~ /(?i)^\<\/BODY/ ) && ( $next_s
=~ /(?i)^\<\/HTML/ ) ) ) {
$is_content = 0;
}
if ( $is_content ) {
# В зависимости от типа строки генерируем необходимый элемент
$str_type = def_str_type($count-1);
if ( $str_type == 1 ) {
$xml_block = $xml_block.generate_record("", "", "", extract_data($cur_s),
$cur_index);
}
elsif ( $str_type == 2 ) {
$id_in = md5_base64($cur_s);
$part_in = 0;
$xml_block = $xml_block.generate_record("", $id_in, $part_in,
extract_data($cur_s), $cur_index);
}
elsif ( $str_type == 21 ) {
$part_in += 1;
$xml_block = $xml_block.generate_record("", $id_in, $part_in,
extract_data($cur_s), $cur_index);
}
elsif ( $str_type == 3 ) {
$id_in = md5_base64($cur_s);
$xml_block = $xml_block.generate_record(1, $id_in, "", "",
extract_topic($cur_s), $cur_index);
}
elsif ( $str_type == 30 ) {
$id_in = md5_base64($cur_s);
($var, $topic) = extract_data($cur_s);
$xml_block = $xml_block.generate_record(1, $id_in, "", "",
$topic, $cur_index);
$xml_block = $xml_block.generate_record("", $id_in, "", extract_data($cur_s),
$cur_index);
}
elsif ( $str_type == 31 ) {
$xml_block = $xml_block.generate_record("", $id_in, "", extract_data($cur_s),
$cur_index);
}
else {
print $cur_s."\n";
}
}
# Сейчас начнутся ссылки на материал
if ( ! $is_content && ( ( $cur_s =~ /(?i)^\<BR/ ) && ( $prev_s =~
/(?i)^\<strong/ ) ) ) {
$is_content = 1;
$index_block = generate_index($cur_index, extract_index($prev_s));
}
$prev_s = $cur_s;
$cur_s = $next_s;
$count += 1;
}
return ($index_block, $xml_block);
}
# Выбираем по одной строки из файла со списком - определяем имя
# обрабатываемого файла.
open(F_LIST, $f_list) || die "cannot open $f_list for reading: $!";
while (<F_LIST>) {
chomp;
# Вызыаем процедуру конверта для данного файла
($t_xi, $t_xc) = convert_one($_);
# Накапливаем полученные результаты
$xml_index = $xml_index.$t_xi;
$xml_content = $xml_content.$t_xc;
}
close(F_LIST) || die "can't close $f_list: $!";
# Ставим закрывающие теги в блоках
$xml_index = $xml_index." </indexes>\n";
$xml_content = $xml_content." </content>\n";
# Формируем выходной файл
open(F_OUT,">".$f_out) || die "cannot open $f_out for write: $!";
print F_OUT "<lindocs>\n";
print F_OUT $xml_index;
print F_OUT $xml_content;
print F_OUT "</lindocs>\n";
close(F_OUT) || die "can't close $f_out: $!";
end genrate.pl В качестве входноых параметров для него передается имя файла со
списком обрабатываемых файлов и выходной файл, в который будет помещено
получившееся xml-дерево.
теперь вы можете поупражняться в создании XSLT преобразований... на
благое дело, кстати...