© Иван Рощин, Москва
| ZXNet | : 500:95/462.53 |
| : bestview@mtu-net.ru | |
| WWW | : http://www.ivr.da.ru |
(«Радиомир. Ваш компьютер» 12/2005)
Описанный в этой статье скрипт позволяет создать электронный справочник (или словарь, или энциклопедию), который может быть доступен как в Интернете, так и локально на вашем компьютере. Скрипт очень прост и подробно прокомментирован. При необходимости его можно дополнять, расширяя его возможности.
Создание и справочника, и словаря, и энциклопедии происходит одинаково, разница только в содержимом, поэтому далее я буду для краткости писать просто «справочник», имея в виду и то, и другое, и третье.
Как выглядит работа с таким справочником? Пользователь набирает в адресной строке браузера адрес скрипта, после чего появляется форма для ввода запроса (рис. 1). Введя нужный запрос, пользователь нажимает кнопку «Найти» (либо клавишу Enter, когда курсор стоит в поле ввода). Если для введённого запроса найдено соответствие в справочнике, скрипт выдаёт соответствующую информацию (рис. 2, 3), иначе выдаёт сообщение о безуспешности поиска (рис. 4).
![]() |
![]() |
![]() |
![]() |
Теперь рассмотрим всё это, так сказать, изнутри. Ниже приведён текст скрипта.
#!usr/bin/perl -w
use strict;
use CGI qw(:standard);
my $script_name='ref.pl'; # Имя файла скрипта.
my $data_name='data.txt'; # Имя файла с содержимым справочника.
my $q=param('q'); # Запрос пользователя.
my $separator='~'; # Разделитель ключа и значения записи.
my $record; # Содержимое текущей записи.
# Если переменная $q не определена (то есть запроса не было),
# приравниваем её пустой строке (вообще-то она и без того
# интерпретировалась бы как пустая строка; явное присваивание
# нужно, чтобы Perl не выдавал предупреждений).
unless (defined ($q)) {$q=''}
# Заменяем в тексте запроса символы "&", "<", ">", '"' на их
# HTML-эквиваленты. Именно в таком виде текст запроса должен
# быть помещён в формируемый HTML-документ, и именно в таком
# виде представлено содержимое файла с записями.
$q=~s/&/&/g; # Именно эта замена должна быть первой!
$q=~s/</</g;
$q=~s/>/>/g;
$q=~s/"/"/g;
# Выводим HTML-документ, который будет возвращён пользователю.
# Начальное содержимое поля ввода берётся из переменной $q: если
# запроса не было - там пустая строка, иначе - текст запроса.
print <<END_OF_HTML;
Content-Type: text/html; charset=windows-1251
Expires: Thu Jan 1 00:00:00 1970
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="ru">
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=windows-1251">
<title>Справочник</title>
<style type="text/css">
<!--
body {background: white; color: black; padding: 1em}
p {text-align: justify; text-indent: 2em; margin-top: 0;
margin-bottom: 0}
-->
</style>
</head>
<body>
<div align="center">
<h1>Справочник</h1>
<form action="$script_name" method="get">
<input type="text" name="q" size="50" value="$q">
<input type="submit" value="Найти">
</form>
</div>
END_OF_HTML
# Если запрос был, ищем соответствующую запись в файле
# с содержимым справочника.
if (defined (param ('q')))
{
open (DATA,'<',$data_name) ||
die ("Can't open file '$data_name'! $!\n");
SEARCH_AND_OUT:
{
while ($record=<DATA>) # Пока файл не кончится.
{
if ($record eq "\n") {next} # Пустые строки пропускаем.
# Прочитана первая строка записи. Если эта строка
# заканчивается на "\", то удаляем этот "\" и добавляем к ней
# следующую строку. Так повторяем, пока очередная прочитанная
# строка не окажется без "\" в конце - это будет последняя
# строка записи.
while ($record=~s/\\\n//)
{
$record.=<DATA>;
}
# Итак, вся текущая запись прочитана в $record. Ищем в ней
# разделитель - он отделяет ключ от значения (например, если
# это символ "~", то в записи "file~file - файл" ключом будет
# "file", а значением - "file - файл"). Если разделитель
# не найден - значит, в файле с записями - синтаксическая
# ошибка, тогда выводим сообщение об этом и прерываем
# выполнение скрипта.
($record=~m/$separator/o) ||
die ("Syntax error in file '$data_name', line $.:".
" separator not found in record!\n");
# Теперь во встроенной переменной $` - то, что было в записи
# до разделителя (то есть ключ), а во встроенной переменной
# $' - то, что было после разделителя (то есть значение).
# Проверяем, совпадает ли ключ с запросом.
if ($` eq $q)
{
# Совпадение обнаружено.
# Выводим значение и выходим из блока SEARCH_AND_OUT.
print ($');
last SEARCH_AND_OUT;
}
}
# Все записи просмотрены, но совпадения не было. Выдаём
# сообщение о безуспешности поиска.
print <<END_OF_HTML;
<div align="center">
К сожалению, по запросу «$q» ничего
не найдено.
</div>
END_OF_HTML
} # Конец блока SEARCH_AND_OUT.
close (DATA) || die ("Can't close file '$data_name'! $!\n");
}
# Завершаем формирование HTML-документа.
print <<END_OF_HTML;
</body>
</html>
END_OF_HTML
Для работы со скриптом на своём компьютере (локально) вам понадобится Perl
(я использовал ActivePerl 5.8.6.811 http://www.ActiveState.com/ActivePerl/),
программа
По умолчанию предполагается, что имя файла со скриптом
«ref.pl» (от «reference book»
«справочник»). Это имя хранится в переменной $script_name (см.
начало скрипта). Именно значение этой переменной подставляется в качестве
адреса обработчика формы в
Непосредственно содержимое справочника хранится в отдельном файле, формат
которого описан ниже. Имя этого файла по умолчанию
«data.txt», оно хранится в переменной $data_name. Вы, конечно,
можете назвать этот файл
Файл с содержимым справочника текстовый, он состоит из записей, каждая из которых начинается с новой строки. Количество записей не ограничивается. Между записями может быть любое число пустых строк. Каждая запись может занимать одну или несколько строк, в последнем случае все строки записи, кроме последней, должны заканчиваться символом «\» (скрипт при чтении такой многострочной записи уберёт эти «\» и склеит все её строки в одну).
Пример расположения записей в файле:
запись 1 начало записи 2\ продолжение записи 2\ окончание записи 2 запись 3
Если запись сама должна заканчиваться на «\», то, чтобы предотвратить неправильное понимание этого «\» как признака «продолжение на следующей строке», надо поставить после него ещё один «\» и после этой записи добавить пустую строку. Тогда при чтении этой записи просто произойдёт её склеивание с добавленной пустой строкой, что не изменит запись.
Каждая запись состоит из трёх частей, следующих друг за другом: ключа,
разделителя (по умолчанию это символ «~», но можно использовать
для этого и
Значение найденной записи становится частью
В значениях записей могут присутствовать элементы
Ниже приведён пример файла с содержимым справочника. Именно с
использованием этого файла получены
sna~<p><b>Расширение:</b> sna</p>\ <p><b>Комментарий:</b> snapshot (файл, в котором \ запоминается состояние эмулируемого «Спектрума»: \ содержимое памяти, значения регистров процессора и \ некоторая другая информация).</p>\ <p><b>Программа для обработки:</b> File Extractor 1.1b.\ </p>\ <p><b>Происхождение расширения:</b> от «snapshot\ ».</p>\ <p><b>Информация о формате или ссылка на неё:\ </b> описание формата SNA 48K — см. \ документацию к эмулятору JPP; описание отличий формата \ SNA 128K от 48K — см. документацию \ к эмулятору UKV (его можно найти, например, на сайте \ Virtual <nobr>TR-DOS —</nobr> <a href=\ "http://zx.da.ru">http://zx.da.ru</a>).</p> trd~<p><b>Расширение:</b> trd</p>\ <p><b>Комментарий:</b> образ диска <nobr>TR-DOS.</nobr></p>\ <p><b>Программа для обработки:</b> File Extractor 1.1b.\ </p>\ <p><b>Происхождение расширения:</b> первые три буквы \ аббревиатуры <nobr>«TR-DOS».</nobr></p>\ <p><b>Информация о формате или ссылка на неё:\ </b> в файле хранится содержимое всех секторов диска \ <nobr>TR-DOS,</nobr> <nobr>без какой-либо</nobr> \ дополнительной служебной информации.</p>
Кстати, при чтении каждой записи скрипт проверяет наличие в ней разделителя и при его отсутствии сразу же прерывает работу, выводя сообщение о синтаксической ошибке в файле с записями (с указанием номера строки в этом файле, при анализе которой ошибка была обнаружена). Это можно использовать для проверки файла с записями: если указать запрос, заведомо не соответствующий никакой записи, то скрипт будет читать все записи, чтобы убедиться в отсутствии искомой, и таким образом проверит их все. Замечу, впрочем, что ошибка не будет обнаружена, если разделитель отсутствует в том месте записи, где он должен быть, но встречается в другом месте записи.
Такое сообщение об ошибке может появиться и тогда, когда в многострочной
записи вы забыли поставить символ «\» в конце
При необходимости можно изменить дизайн
Строки
Приведённый вариант скрипта рассчитан на то, что кодировка и его самого, и
файла с записями
И ещё одно замечание. При большом размере файла с записями поиск в нём
может занять длительное время, ведь в худшем случае (если искомой записи в
этом файле нет или если эта запись последняя) будет просмотрен весь файл. Для
ускорения поиска можно, например, разбить файл с записями на отдельные файлы,
в каждом из которых будут записи с ключами, начинающимися на
«свою» букву, и производить поиск лишь в одном из этих файлов, в
зависимости от первой буквы запроса. Или можно использовать