Распечатать статью

Поиск на php

95% бесплатных php-скриптов (и не только php) — полный «отстой». Оно и понятно: хороший программист бесплатно ничего писать не будет, а если и будет, то только в свободное время в качестве развлечения, и уж, конечно не всякие банальности, вроде гостевых книг. Или, как говорил Рома Воронежский: «Вот проблема с этими творческими людьми: они всегда желают быть композиторами, художниками и писателями. В результате производством труб большого диаметра занимаются бездарности».

Именно так это и происходит.

Сегодня опять ковырялся в каталогах бесплатных скриптов, главным образом из любопытства, но еще и в тайной надежде найти что-нибудь забавное. В прошлый раз из «забавных» скриптов я нашел, например, «скрипт вывода текстового файла в php». Думал — парсер. Оказалось — да: почти что парсер. Привожу скрипт целиком: «?php include («text.txt»); ?». Или вот вижу скрипт, написано «This script will reverse the text you give it. reversed: .ti evig uoy txet eht esrever lliw tpircs siht It isn’t very useful, it’s just funny. Try it out :)», то есть скрипт переворачивает строку задом-наперед. Самые худшие ожидания оправдались: они делали это циклом. Наверное, не знали, что в php есть специальная, уже готовая функция для этого:

Нашел скрипт поиска по сайту: он обшаривает директории, которые вы указали, открывает все html-файлы и тупо сравнивает: У меня тоже такой же скрипт был давным-давно написан, но потом, когда я понял, что народ поиском все-таки пользуется (сюрприз!), решил сделать его по-человечески. С индексом и прочими благами цивилизации: Сделал. В результате 1.5 мегабайта заметок превращаются в 900-килобайтный индекс за 17 секунд (индексацию надо проводить раз в несколько дней, или даже реже — в зависимости от скорости обновления сайта), после чего поиск по индексу происходит меньше одной секунды.

В общем, решил я все-таки поделиться этим скриптом. Краткая информация: скрипт на php, для работы никаких mysql не надо, предполагается, что html-ные (или txt-овые, как у меня) файлы где-то лежат, а не хранятся в mysql. В общем, поисковая машина для небольшого (ну, или «среднего») сайта. Как, например, spectator.ru.

Итак, начали:

Самое первое — скрипт индексации. Для чего он нужен?.. Вот у меня 278 заметок. Если мы будем открывать каждый файл и искать совпадения, то нам надо будет открыть 278 файлов. А это ой как долго: Более того, нам надо будет 278 раз провести хитрые манипуляции с этими файлами (про манипуляции — ниже). Если же у нас есть индекс, то во-первых, поиск происходит в одном файле (индексе), во-вторых, все эти «хитрые манипуляции» уже выполнены.

Алгоритм индексирующего скрипта такой:
Открываем очередной файл
Убираем из него «мусор» ( зачем убирается мусор — понятно, чем мусора меньше, тем ищется быстрее: ):
переводы строк
html-тэги
знаки препинания
слова, короче трех букв (а зачем они там?)
Делаем заглавные буквы строчными.
Убираем повторяющиеся слова. (Действительно, зачем нам вся это тавтология?)
Записываем все в индекс.
Если еще есть файлы, переходим к пункту 1.

Реализуется это все на php — легко!

// Spectator’s Indexing Script
// (C) Spectator.ru
// Для работы требуется PHP 4 или выше.
// Если вы будете использовать этот скрипт, ссылка на Spectator.ru
// крайне желательна. Спасибо.

// ставим скрипт «на счетчик» (чтобы знать, как долго он выполнялся
$ttt=microtime();
$ttt=((double)strstr($ttt, ‘ ‘)+(double)substr($ttt,0,strpos($ttt,’ ‘)));

$indexdir=»text»; #индексируемая директория
$indexfile=»indexfile.txt»; #файл, в котором будет лежать индекс
// если вы хотите индексировать файлы в нескольких директориях, надо
// внести несколько махоньких добавлений…

// делаем так, чтобы не было таймаута из-за того, что скрипт будет долго
// выполняться (на всякий случай) и из-за того, что пользователь нажмет
// кнопку «стоп» в браузере

$abort = ignore_user_abort(1);
set_time_limit(600);

// Функция, удалающая слова, короче 3х букв. Пригодится дальше.
function sw (&$item1, $key) { if (strlen($item1)<3) $item1=»»; }

// по очереди открываем все файлы в директории и проверяем, можно ли их
// индексировать у меня можно индексировать только файлы, которые имеют
// вид «число.txt» то есть && (is_numeric(str_replace («.txt»,»», $file)))
// это вам наверняка не понадобится.

$handle=opendir(‘./’.$indexdir);
while (false!==($file = readdir($handle))):
if ($file!=».» && $file!=»..» && (is_numeric(str_replace («.txt»,»», $file)))):

// открываем очередной файл
$fd = fopen ($indexdir.»/».$file, «r»);
$contents = fread ($fd, filesize ($indexdir.»/».$file));
Fclose ($fd);

// убираем переводы строк
$contents=str_replace («n»,» «, $contents);
$contents=str_replace («r»,»», $contents);

// убираем хтмл-тэги
$contents=str_replace (‘
‘, ‘ ‘, $contents);
$contents=str_replace (‘

‘, ‘ ‘, $contents);
$contents=strip_tags ($contents);

// убираем знаки препинания и цифры
// все эти строки работают быстрей, чем один eregi_replace!

$contents=str_replace (‘ -‘, ‘ ‘, $contents);
$contents=str_replace (‘.’, ‘ ‘, $contents);
$contents=str_replace (‘,’, ‘ ‘, $contents);
$contents=str_replace (‘!’, ‘ ‘, $contents);
$contents=str_replace (‘?’, ‘ ‘, $contents);
$contents=str_replace (‘:’, ‘ ‘, $contents);
$contents=str_replace (‘;’, ‘ ‘, $contents);
$contents=str_replace (‘)’, ‘ ‘, $contents);
$contents=str_replace (‘(‘, ‘ ‘, $contents);
$contents=str_replace (‘»‘, ‘ ‘, $contents);

// убираем заглавные буквы
$contents=strtolower ($contents);

// разбиваем на слова, убираем слова, короче 3х букв
$contents=explode (» «, $contents);
// вот и функция пригодилась…
array_walk ($contents, ‘sw’);

// убираем повторяющиеся слова
$contents=array_unique ($contents);

// соединяем слова
$contents=implode (» «, $contents);

// формируем соответствующую строку в индексе.
$fullfile.=$file.»| «.$contents.» n»;

// индекс-файл будет иметь вид:
// имя_файла|индекс_для_данного_файла n
// имя_файла|индекс_для_данного_файла n
// имя_файла|индекс_для_данного_файла n

echo ($file.» проиндексирован
«);
// переходим к следующему файлу

endif;
endwhile;
closedir($handle);

// убираем двойные пробелы
while (stristr($fullfile, » «)) $fullfile=str_replace (» «,» «,$fullfile);

// индекс готов, сохраняем его
$fp = fopen($indexfile, «w+»);
fwrite($fp, $fullfile);
fclose($fp);

// считаем, как долго работал скрипт
$ddd=microtime();
$ddd=((double)strstr($ddd, ‘ ‘)+(double)substr($ddd,0,strpos($ddd,’ ‘)));

echo (»
Время индексации: «.(number_format(($ddd-$ttt),3)).
» секунд
«);
echo («Размер индекса: «.(number_format((round ((filesize($indexfile))/1024)) ,
0, «.»,».»))).» Kb»;

?>

Итак, у нас есть индекс. Дальше — просто. Так ведь?.. Надо просто произвести поиск в нем. Берем функцию eregi, например:

Хотя я делал совсем по-другому:

Лирическое отступление: часто, когда надо проверить, если в строке какая-нибудь комбинация символов, пишут что-то вроде этого:
if (eregi(‘this must be found’,$string))
echo ‘found!!';
else
echo ‘нифига не found!';

Способ хороший, но тормозной — из-за eregi. (Функция это работает с регулярными выражениями, поэтому и тормозит). По той же причине рекомендуется использовать там, где это можно, str_replace вместо ereg_replace. Быстрее раз в 10: Поэтому крутые программеры ;), когда им надо проверить, найдено ли что-то в строке, используют функцию strstr. На самом деле, она для этого не предназначена, (верней, «предназначена не для этого»), ибо она «Find first occurrence of a string», то есть «ищет первое местонахождение строки» и выводит строку, начиная с этого самого местоположения. Запутал, верно? (смайлик).

Ок, вот пример с php.net:
$email = ‘sterling@designmultimedia.com';
$domain = strstr ($email, ‘@’);
print $domain;
// выводит: @designmultimedia.com

Теперь понятно? Функция ищет, где в строке встречается подстрока «@» и выводит все после нее (включительно). Что самое главное — если ничего на найдено, то функция возвращает false. Именно поэтому ее можно использовать вот так:
if (stristr($string, ‘this must be found’))
echo ‘found!!';
else
echo ‘нифига не found!';

У меня в скрипте для поиска в индексе используется stristr. Кроме того, поиск понимает простейший синтаксис: «+» (слово должно быть найдено, aka AND), «-» (слово не должно быть найдено, aka NOT) и «*» (звездочка). Но, анализируя то, что искали у меня на сайте, могу сказать только одно: Где-то в дискуссии про поисковые машины и их AI (искусственный интеллект), я нашел такую фразу, что «проще выучить последнего дебила пользоваться языком запросов, чем научить поисковую машину угадывать, что же именно этому дебилу надо/». Действительно, запрограммировать поисковую систему так, чтобы она сразу же выдавала то, что надо по идиотским запросам — сложно. Но, похоже, обучить ИХ составлять запросы правильно еще сложней:

В моем поиске всеми этими значками никто не пользуется, как бы я ни распинался. Хотя, когда мне надо найти у себя что-то конкретное, я нахожу с первого запроса (да, конечно, я ведь примерно знаю, что искать, и сам писал скрипт поиска, но все-таки:)

Скрипт простой, но работает надежно. Если правильно составить запрос — то находит все с первого раза. В принципе, можно еще сделать сортировку по релевантности, сделать так, чтобы из найденного файла показывался кусок с текстом, где искомое слово было бы выделено, и прочее: Но это вы делайте сами:

// Spectator’s Site Search Script
// (C) Spectator.ru
// Для работы требуется PHP 4 или выше.
// Если вы будете использовать этот скрипт, ссылка на Spectator.ru
// крайне желательна. Спасибо.

// файл с индексом
$indexfile=»indexfile.txt»;

// обрабатываем запрос

$total=0;
$qu2=str_replace («+»,»&»,$words);

// убираем заглавные буквы
$qu2=strtolower ($qu2);

// обрубаем в конце лишние пробелы
$qu2=chop($qu2);

// убираем двойные пробелы
while (stristr($qu2,» «)) $qu2=str_replace (» «,» «,$qu2);

echo (‘Запрос: ‘.$qu2);
echo (‘

‘);

// разбиваем запрос на слова
$words = explode (‘ ‘, $qu2);

// удаляем в запросе все лишнее (знаки препинания, и прочее)
$qu2=eregi_replace (‘[.?,!()#»:;|]’, », $qu2);

// проверяем длинну запроса
if (strlen($qu2)>2):

// открываем индекс
$index=file ($indexfile);
$num= (count($index)-1);

// для каждой сточки из индекса (одна строчка=один файл) выполняем:
for ($i=1; $i<$num+2; $i++):

$contents=$index[$i-1];

$wordcount =0;
$mustfound=1;

// выполняем для каждого их запрошенных слов:
$mustntfound=1;

for ($q=0; $q
// обработка знаков *, + и —

// знак *
if (stristr($words[$q], «*»)) {$search=str_replace («*»,»»,$words[$q]); }
else { $search=» «.$words[$q].» «;}

// если в слове есть звездочка, то убираем звездочку и добавляем в начало
// и конец слова по пробелу если нет проблелов, то слово будет искаться не
// целиком, а «вообще», то есть на запрос «чай» будет выводиться и слово
// «случайный».

// bug: скрипт не учитывает, где в слове стоит звездочка и считает, что в
// любом случае она стоит в конце (!!)

// знак & (или +)
if (stristr($search, «&»)) {$search=str_replace («&»,»»,$search); $mustfound++; }

// если стоит знак +, то количество слов, которые _должны_ быть найдены,
// увеличиваются на 1

// знак —
if (stristr($search, «-«)) {
$search=str_replace («-«,»»,$search);
$mustntfound=0;
}

// если стоит знак +, то если слово найдено, весь результат умножается на 0
// (смотри дальше).

// если слово найдено, считаем его и умножаем на $mustntfound, то есть на 1,
// если найдено «правильное» слово и на 0, если найдено слово, помеченное
// знаком —

if (stristr($contents, $search)) {
$wordcount++;
$wordcount=$wordcount*$mustntfound;
}

endfor;

// проверяем, все ли слова, помеченные знаком + найдены,
// либо (если таких слов нет), найдено ли вообще хоть одно слово

if ($wordcount >= $mustfound):

// находим имя файла, в котором это найдено
$file=explode («|»,$contents);
$file=$file[0];

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

$file=str_replace («.txt», «», $file);
$file=str_replace («_», «.», $file);
echo (««.$file.»
«);

// считаем, колько всего файлов надено
$total++;
endif;

// переходим к следующемуу файлу
endfor;

// выводим результаты
if ($total!=0) echo (‘

Всего найдено страниц: ‘.$total);
else echo («Ничего не найдено!

Возможно, вы».
.» просто не правильно составили запрос. Как это сделать правильно».
.» — смотрите вот здесь.»);

else:
echo (»
Слишком короткий запрос!»);
endif;

?>
Источник:   codingrus.ru

Вы можете оставить комментарий, или обратную ссылку на Ваш сайт.

Оставить комментарий

Похожие статьи