Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Поиск на php

© http://spectator.ru/
28 июня 2001 года

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 раз провести хитрые манипуляции с этими файлами (про манипуляции - ниже). Если же у нас есть индекс, то во-первых, поиск происходит в одном файле (индексе), во-вторых, все эти «хитрые манипуляции» уже выполнены.

Алгоритм индексирующего скрипта такой:

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

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

<?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 ('<br>', ' ', $contents);
$contents=str_replace ('<p>', ' ', $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." проиндексирован<br>");
// переходим к следующему файлу

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 ("<br>Время индексации: ".(number_format(($ddd-$ttt),3)).
      " секунд<br>");
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 (искусственный интеллект), я нашел такую фразу, что «проще выучить последнего дебила пользоваться языком запросов, чем научить поисковую машину угадывать, что же именно этому дебилу надо/». Действительно, запрограммировать поисковую систему так, чтобы она сразу же выдавала то, что надо по идиотским запросам - сложно. Но, похоже, обучить ИХ составлять запросы правильно еще сложней:

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


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

<?php

// 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 ('<p><p><p>');

// разбиваем запрос на слова
$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<count($words); $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 ("<a href=".$file.">".$file."</a><br>");


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

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

// выводим результаты
if ($total!=0) echo ('<br><br>Всего найдено страниц: '.$total);
else echo ("<b>Ничего не найдено!</b><p>Возможно, вы".
          ." просто не правильно составили запрос. Как это сделать правильно".
          ." - смотрите <a href=search>вот здесь</a>.");

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


?>

<!-- Форма для поиска: -->
<form method=get action=search.php>
<input type=text size=19 name=words value="" maxlength=150>
<input type=submit class=frm value=Go>
</form>

PS. PHP.net - очень хороший сайт. 99% того, что мне надо, я нахожу там. Все просто: если вам не понятно, как работает какая-то функция, достаточно просто ввести ее в поле поиска на том сайте и вам выдадут описание функции, но кроме того, на php.net к описанию каждой функции пользователи могут оставлять комментарии, поэтому вам выдадут и гору толковых комментариев. То, что надо.

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

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 

Комментарии

1.
40K
27 мая 2008 года
diclofoss
0 / / 27.05.2008
+0 / -1
Мне нравитсяМне не нравится
27 мая 2008, 12:27:41
Взял от сюда разброр слова... вопрос: ишу слово "дети".
Вырезаю последнюю гласную остается "дет" после этого ищу value LIKE % дет%
в результате находит детали. Есть какой нить алгоритм поиска корня слова? я просто смотрю на этом же сайте он никогда не перепутает детей с деталями.
2.
36K
11 января 2008 года
andriykm
0 / / 11.01.2008
+1 / -0
Мне нравитсяМне не нравится
11 января 2008, 16:21:19
что зделать чтобы скрипт искал в каталогах?
3.
3.9K
07 июня 2005 года
АКМ
49 / / 07.06.2005
+0 / -1
Мне нравитсяМне не нравится
6 января 2008, 00:06:09
скрипт плохо выполняет или не выполняет совсем удаление повторяющихся слов.
4.
32K
13 августа 2007 года
caezaris
0 / / 13.08.2007
Мне нравитсяМне не нравится
13 августа 2007, 04:19:35
Совет автору более тщательно читать PHP Manual (!), в котором если не ошибаюсь по поводу этого даже отдельные замечания есть:

stristr('yo','yo');
stristr('yo0',yo);
5.
1.9K
19 ноября 2004 года
xexsus
33 / / 19.11.2004
+0 / -1
Мне нравитсяМне не нравится
10 марта 2007, 20:06:11
> string chop(string str); - Возвращает строку без повторяющихся пробелов.

враки
6.
22K
25 сентября 2006 года
Максимэн
0 / / 25.09.2006
+0 / -1
Мне нравитсяМне не нравится
25 сентября 2006, 08:58:24
<?php
$ttt=microtime();
$ttt=((double)strstr($ttt, ' ')+(double)substr($ttt,0,strpos($ttt,' ')));
$abort = ignore_user_abort(1);
set_time_limit(600);

function sw (&$item1, $key) { if (strlen($item1)<3) $item1=""; }
setlocale(LC_ALL, 'ru_RU.utf8');
if ($handle = opendir('service/pages')){
while ($file = readdir($handle)){
if ($file != "." && $file != ".." && !is_dir($file)){
$fd = fopen ("service/pages/".$file, "r");
if(!filesize("service/pages/".$file)==0){
$contents = fread ($fd, filesize ("service/pages/".$file));
fclose ($fd);
}
$contents=str_replace ('require_once("../../class2.php")', '', $contents);
$contents=str_replace ('require_once(FOOTERF)', '', $contents);
$contents=str_replace ('require_once(HEADERF)', '', $contents);
$contents=str_replace ('class2', '', $contents);
$contents=str_replace ('$aj = new textparse', '', $contents);
$contents=str_replace ('$caption = $aj -> tpa($caption, "on")', '', $contents);
$contents=str_replace ('$text = $aj -> tpa($text, "on","admin")', '', $contents);
$contents=str_replace ('$ns -> tablerender($caption, $text)', '', $contents);
$contents=str_replace ('$caption =', '', $contents);
$contents=str_replace ('$text =', '', $contents);
$contents=str_replace ('0', '', $contents);
$contents=str_replace ('1', '', $contents);
$contents=str_replace ('2', '', $contents);
$contents=str_replace ('3', '', $contents);
$contents=str_replace ('4', '', $contents);
$contents=str_replace ('5', '', $contents);
$contents=str_replace ('6', '', $contents);
$contents=str_replace ('7', '', $contents);
$contents=str_replace ('8', '', $contents);
$contents=str_replace ('9', '', $contents);
$contents=str_replace ('/','', $contents);
$contents=str_replace ('<br>', ' ', $contents);
$contents=str_replace ('</br>', ' ', $contents);
$contents=str_replace ('<p>', ' ', $contents);
$contents=str_replace ('</p>', ' ', $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=str_replace (')', ' ', $contents);
$contents=str_replace ('(', ' ', $contents);
$contents=str_replace ('"', ' ', $contents);
$contents=strip_tags ($contents);
$contents=str_replace ('&quot', ' ', $contents);
$contents=str_replace ('&nbsp', '', $contents);
$contents=str_replace ('justify', '', $contents);
$contents=str_replace ("link","", $contents);
$contents=str_replace ('bode', '', $contents);
$contents=str_replace ('году', '', $contents);
$contents=str_replace ('года', '', $contents);
$contents=str_replace ('php', '', $contents);
$contents=str_replace ('sevice', '', $contents);
$contents=str_replace ('pages', '', $contents);
$contents=strtolower ($contents);
$contents=explode (" ", $contents);
array_walk ($contents, 'sw');
$contents=array_unique ($contents);
$contents=implode (" ", $contents);
$fullfile.=$file."| ".$contents." \n";
echo ($file." проиндексирован<br>");
}
}
closedir($handle);
$fp = fopen("indexfile.txt", "w+");
fwrite($fp, $fullfile);
fclose($fp);

$ddd=microtime();
$ddd=((double)strstr($ddd, ' ')+(double)substr($ddd,0,strpos($ddd,' ')));
echo ("<br>Время индексации: ".(number_format(($ddd-$ttt),3))." секунд<br>");
}
?>
7.
Аноним
Мне нравитсяМне не нравится
7 февраля 2006, 15:12:15
Скрипт немного громоздкий... Например, следующие строки

$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 = preg_replace('{[-.,!?:;)("]}is', ' ', $contents)

или даже так

$contents = preg_replace('{[[:punct:]]}is', ' ', $contents)

и это не единственный пример. см. описание http://ru2.php.net/preg_replace

----------------------

Видимо, тоТ, кто писал этот скрипт, не слишком-то посвящен в особенности функции stristr (см http://ru2.php.net/stristr). Функция возвращает false, если не найдено ничего. Но она также может возвратить и 0. Если это произойдет (функция возвратит 0), то следующие строки:


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

не посчитают слово найденным, в то время как оно найдено! Ведь с точки зрения php 0 == false, но 0 !== false! Обратите на это внимание!

Так что... Не все скрипты здесь будут работать хорошо... Подумайте прежде чем что-то писать или качать. А так же больше тестируйте проги. :)

С наилучшими пожеланиями, Чучундер.
8.
Аноним
Мне нравитсяМне не нравится
15 декабря 2005, 07:45:32
Полезная статья. Особенно приятно, что люди писали правильные и конструктивные комментарии.
9.
Аноним
Мне нравитсяМне не нравится
19 сентября 2005, 15:12:37
По поводу повторяющихся пробелов:
string chop(string str); - Возвращает строку без повторяющихся пробелов.

10.
Аноним
+0 / -1
Мне нравитсяМне не нравится
19 сентября 2005, 15:01:33
Ещё если файл который мы нашли, пустой, то на строке "$contents = fread ($fd, filesize ($indexdir."/".$file));"
возникнет warning "Warning: fread(): Length parameter must be greater than 0", предлагаю добавить проверку на то что filesize($my_dir.$file)!=0.
Так в фелом скрипт косный мне очень помог. Спасибо.
11.
Аноним
Мне нравитсяМне не нравится
19 сентября 2005, 14:46:28
А что если в директории, в которой ищем файлы есть ещё директории, то возникнет ошибка?
Предлагаю вместо if ($file!="." && $file!="."...) писать
if(!is_dir($file)).
12.
Аноним
Мне нравитсяМне не нравится
10 сентября 2005, 23:28:57
Ваш скрипт индексирует только статичесткие страницы, коих в Инете почти не осталось...
13.
Аноним
Мне нравитсяМне не нравится
25 августа 2005, 17:07:00
to Zoja
Нерусский Апач настроить можно.
В скрипте вызвать:
setlocale(LC_ALL, 'ru_RU.utf8');
14.
Аноним
Мне нравитсяМне не нравится
28 июля 2005, 18:31:25
замечен код:
...
$contents=str_replace (' -', ' ', $contents);
$contents=str_replace ('.', ' ', $contents);
$contents=str_replace (',', ' ', $contents);
...

и есть функция strtr

лучше будет читатся/выполнятся
$contents=strtr($contents,array('-'=>'','.'=>'',.....));
15.
Аноним
+1 / -0
Мне нравитсяМне не нравится
15 июля 2005, 16:56:53
vИндексный скрипт циклится на
// убираем двойные пробелы
while (stristr($fullfile, " ")) $fullfile=str_replace (" "," ",$fullfile);

Пытается заменить 1 пробел на 1 пробел. Надо писать:
// убираем двойные пробелы
while (stristr($fullfile, " ")) $fullfile=str_replace (" "," ",$fullfile);
16.
Аноним
Мне нравитсяМне не нравится
17 июня 2005, 12:59:16
Код:
$qu2=strtolower ($qu2);

Никакие функции не работают с регистром русских букв в "нерусском" апаче. Есть ли решения?
17.
Аноним
Мне нравитсяМне не нравится
19 мая 2005, 14:08:49
не работает, а жаль - очень пригодился бы :(
18.
Аноним
Мне нравитсяМне не нравится
18 мая 2005, 14:09:30
Скрипт написан вроде грамотно, но останавливается на процессе индексации, нужно править.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог