. ........ . ........ .  Январь / 2000
. Стандарт CGI (Common Gateway Interface) изначально был разработан для того, чтобы дать возможность пользователям запускать программы, доступные на сервере, через Web. Первые CGI программы служили простым интерфейсом для стандартных команд grep и finger, преобразовывали информацию, выдаваемую этими командами, в формат HTML и передавали полученные результаты броузеру пользователя. . Простая поисковая система.

Поиск на вашем Web - сайте - это просто.
Введение в методику поиска.


Рэйвен М. Лернер
Linux Journal, #69, Январь 2000

Перевод с английского:
Игорь Грень
gren@open.by


CGI программы и прочие программы, выполняемые сервером, с тех пор значительно усложнились. Но одно из возможных применений подобных программ со временем не утратило своей актуальности: возможность поиска среди документов, хранящихся на Web- сайте по ключевому слову или строке. Если поисковые системы (сейчас их называют "порталами") делают возможным поиск по всей сети Интернет, среди огромного количества серверов, CGI программы реализуют упрощенную задачу поиска. Они выполняют поиск в файлах только одного, локального сервера и генерируют список URL к различным документам по запросу пользователя. Рассмотрим способы создания нескольких типов поисковых программ. И хотя эти программы не могут соревноваться с ht://Dig и Webglimpse, они дают возможность разобраться в том, как работают подобные программы и как они создаются. Простой поиск и командная строка Мой любимый язык для создания таких программ - это Perl. В основном, благодаря возможностям обработки текстов, Perl позволяет с легкостью найти один фрагмент текста внутри другого. Например, эта программа, состоящая из одной строки, выводит каждую строку файла test.txt, которая содержит слово "foo":
perl -ne 'print if m/foo/' test.txt
Ключ -n приказывает не выводить все строки по умолчанию, а -e дает нам возможность вставить вызов команды между одиночными кавычками ('). Мы просим Perl выводить каждую строку, в которой оператор m// (совпадение) найдет указанное слово. Выполнение этой задачи мы можем вставить в программу, как показано на Листинге 1.

Listing 1. simple-search-1.pl
#!/usr/bin/perl -w
use strict;
use diagnostics;
# Open the file
my $filename = "test.txt";
open FILE, $filename
or die qq{Cannot read "$filename": $! };
# Iterate through each line of the file,
# printing all lines that match
while (<FILE>)
{
print if m/foo/;
}
close FILE;

Конечно, эта программа выполняет поиск по простой конкретной маске (строка "foo") внутри одного конкретного файла (test.txt). Мы можем расширить поле действия программы, используя пустой указатель <> вместо поиска в <FILE>. Пустой указатель <> просматривает каждый элемент в @ARGV (массив аргументов командной строки), присваивая значение каждого элемента массива переменной $ARGV. Если командная строка не содержит аргументов, <> ожидает ввода данных от пользователя. Листинг 2 содержит модифицированную версию программы, которая выполняет поиск строки "foo" в нескольких файлах. Обратите внимание на то, что эта программа уже выводит вместе с найденной строкой и название файла. Так как $_ уже содержит символ перевода на новую строку, нам не нужно добавлять его в конце каждой команды print. Второй листинг можно тоже свести к одной командной строке вызова Perl: perl -ne 'print "$ARGV: $_" if m/foo/;' *

Listing 2. simple--search--2.pl
#!/usr/bin/perl -w
use strict;
use diagnostics;
# Iterate through each line of each file
while (<>)
{
# Print the matching filename and line
print "$ARGV: $_" if m/foo/;
}

Теперь мы можем усложнить нашу программу и разрешить пользователю указать маску для поиска и названия файлов - объектов поиска. Программа в Листинге 3 берет первый аргумент командной строки, удаляет его из @ARGV и переносит в $pattern. Чтобы Perl понял, что $pattern не изменится и поиск выполняется только один раз, мы используем m// с параметром /o.


Listing 3. simple-search-3.pl
#!/usr/bin/perl -w
use strict;
use diagnostics;
# Get the pattern
my $pattern = shift @ARGV;
# Iterate through each line of each file
while (<>)
{
# Print the matching filename and line
print "$ARGV: $_" if m/$pattern/o;<\n>
}

Теперь для поиска строки f.[aeiou] во всех файлах с расширением "txt" мы используем: ./simple-search-3.pl "f.[aeiou]" *.txt Можете быть уверены, что теперь каждая строка, которая содержит символ f, после которого следует гласная буква, указанная в квадратных скобках, отобразится на экране вместе с именем файла. File::Find Программа, приведенная выше, может служить хорошей основой для Web-поиска, если все документы на Web-сайте хранятся в одном каталоге. Но в реальной жизни большинство Web-сайтов представляет собой весьма разветвленную иерархическую систему подкаталогов, заполненных файлами. Хорошая поисковая программа должна пройтись по всей иерархии Web-сайта, просмотреть каждый файл в каждом подкаталоге. Пока мы старались выполнить эту задачу самостоятельно, кто-то уже поработал за нас. Модуль File::Find, который входит в состав Perl, позволяет создавать программы, аналогичные find, на этом языке. File::Find экспортирует подпрограмму find, которой можно передавать список аргументов. Первый аргумент- это ссылка на подпрограмму, которая вызывается для каждого найденного файла. Остальные аргументы - это названия каталогов и файлов, которые File::Find будет просматривать последовательно, пока не доберется до последнего. Например, в Листинге 4 приведена программа, использующая File::Find для вывода списка файлов, хранимых в конкретном каталоге. Как видно, File::Find экспортирует переменную $File::Find::name, которая содержит текущее имя файла. Название текущего каталога помещается в $File::Find::dir.


Listing 4. simple-find.pl
#!/usr/bin/perl -w
use strict;
use diagnostics;
use File::Find;
# Invoke "find" with a reference to our
# subroutine, and the initial directory name
find(\&print_name, "/home/reuven");
sub print_name { print "$File::Find::name\n"; }


Listing 5: simple-find-2.pl
#!/usr/bin/perl -w
use strict;
use diagnostics;
use File::Find;
# Get the pattern from the input list
my $pattern = shift @ARGV;
# Slurp up the entire contents of a file
$/ = undef;
print qq{Searching for "$pattern".\n};
# Invoke "find" with a reference to our subroutine,
# with the directories passed as arguments
find(\&find_matches, @ARGV);
sub find_matches
{
my $filename = $_;
# Open the file, and search through its
# contents
if (open FILE, $filename)
{
# Get the file
my $contents = (<FILE>);
# If there aren't any contents, then return
# right away
return unless $contents;
# Print the filename, with the directory
print qq{$File::Find::dir/$filename\n}
if ($contents =~ m|\b$pattern\b|is);
close FILE;
}
else
{
warn qq{Unable to open
"$File::Find::dir/$filename": $! };
return;
}
}
В Листинге 5 приведена программа simple-find-2.pl, которая использует File::Find для поиска в файлах, находящихся в каталогах, помещенных в указанный заданный каталог. Как и в других программах, использующих File::Find, основная работа simple-find-2.pl выполняется find_matches, подпрограммой, вызываемой для обработки каждого файла, найденного в каталогах, перечень которых содержит массив @ARGV. Чтобы найти все файлы, содержащие слово "f.[aeiou]" в каталогах /home and /development, печатаем:
./simple-find-2.pl "f.[aeiou]" /home /development
Строка 8 программы simple-find-2.pl имеет особое значение: она переназначает $/, переменную, которая определяет символ конца строки. Обычно оператор <> просматривает файл строка за строкой, возвращая undef при достижении конца строки. Но мы хотим производить поиск во всем файле, даже если искомое слово или фраза будут начинаться на одной строке, а заканчиваться на другой. После переопределения $/, строка my $contents = (<FILE>); переносит все содержимое файла FILE в переменную $contents, а не только одну строку.

Продолжение








Содержание
........
Трехмерные миры в Web: VRML
Виталий Фридман
........
Простая поисковая система
Рэйвен М. Лернер
Linux Journal, #69, Январь 2000

........
Интернет коммерция в Беларуси
Дмитрий Шейко
........
Гиперссылка на любимую телепередачу
Алиса Бизяева
........
Quake. Третье пришествие
Денис Москаленко
........