За последние 24 часа нас посетили 30950 программистов и 1441 робот. Сейчас ищут 860 программистов ...

Анализатор текста(не тривиальная задача)

Тема в разделе "PHP для новичков", создана пользователем Dima4321, 22 янв 2011.

  1. Dima4321

    Dima4321 Активный пользователь

    С нами с:
    1 апр 2009
    Сообщения:
    683
    Симпатии:
    0
    Суть проста-- найти кол-во вхождений 2 любых слов идущих друг за другом применительно к тексту.


    PHP:
    1. <?
    2. ini_set('display_errors',1);
    3.  error_reporting(E_ALL);
    4.  
    5.  
    6.  
    7.  
    8. $new='hello my friend, hello my friend,hello,goodbuy my friend,hello my friend,goodbuy my friend,goodbuy, hello my friend,';
    9.  
    10. $new=preg_replace('/[-,.\;:\'\"\d)(!\s]+/'," ",$new);
    11. $new=preg_replace('/[\s]+/'," ",$new);
    12. echo $new.'<br>';
    13.  
    14. preg_match_all('/\b.+?\b\s\b.+?\b/si',$new,$match);
    15.  
    16. $new=array_count_values($match[0]);
    17.  
    18. arsort($new);
    19.  
    20.  
    21. foreach($new as $key=>$value)
    22. {
    23. if($value>1 and strlen($key)>1)
    24. {
    25. echo $key. '---'.$value.'<br>';
    26. }
    27. }
    28. echo '<pre>';
    29. print_r($new);
    30. echo '<pre>';
    31.  
    32.  
    33.  
    34.  ?>
    Итак имеем повторов my friend --только 4 раза вместо 6. Как воплотить задачу в жизнь..??? надо задавать какое-то новое смещение в регулярке по циклу??

    Вообще что можно сделать..?? как правильно прочитать текст и понять что там больше my friend и так же понимать что там много "hello my" и есть "goodbuy my"

    Буду рад любой помощи.

    Apple ты ведь наверно точно знаешь как решаются подобные задачи ?
     
  2. Apple

    Apple Активный пользователь

    С нами с:
    13 янв 2007
    Сообщения:
    4.984
    Симпатии:
    2
    Да сколько ж можно повторять-то одно и то же, неужели у самих головы на плечах нет: если нет логической последовательности группировки, то задача не имеет решений.
    Один из вариантов - тупой перебор последовательностей с указанным смещением от начала строки, т.е количество переборов будет равно количеству слов в одной группе для первого прохода.

    Для того, чтобы у задачи появились рациональные решения, то для начала нужно саму задачу свести к системе, а не искать решения.
     
  3. Dima4321

    Dima4321 Активный пользователь

    С нами с:
    1 апр 2009
    Сообщения:
    683
    Симпатии:
    0
    Да возможно ты прав...

    раньше пользовался вот этой штукой для больших текстов

    http://www.seotxt.com/service/optimizer/

    и только сейчас ввел свой простой текст и понял, что она фигово ищет фразы.


    Думаю смещение от начала строки на одно или два слова все равно даст мало нужных результатов. Хотя что-то явно совпадет. и этим можно тоже пользоваться.

    тут нужен алгоритм перескакивания через разное кол-во слов с случайной возможностью попадания в нужную фразу.
     
  4. Jampire

    Jampire Активный пользователь

    С нами с:
    22 авг 2009
    Сообщения:
    181
    Симпатии:
    0
    Адрес:
    Гомель
    PHP:
    1. <?php
    2.  
    3. function count_phrase($string, $step = 2)   {
    4.     if ($step <= 0)  {
    5.         die("Step must be greater than 0!");
    6.     }
    7.     global $result;
    8.     if (preg_match('/\w+/s', $string) == 0)  {
    9.         return $result;
    10.     }
    11.     preg_match('/(\W*\w+\W*){'.$step.'}/s', $string, $matches);
    12.     $matches[0] = trim($matches[0]);
    13.     if (!isset($result[$matches[0]]))   {
    14.         $result[$matches[0]] = substr_count($string, $matches[0]);
    15.     }
    16.     $string = trim(preg_replace('/^\w+[[:punct:]]*/s', "", $string, 1));
    17.     count_phrase($string, $step);
    18. }
    19.  
    20. $result = array();
    21. $string = "hello my friend, hello my friend,hello,goodbuy my friend,hello my friend,goodbuy my friend,goodbuy, hello my friend,";
    22.  
    23. echo $string."<br />";
    24. count_phrase($string, 2);
    25. print_r($result);
    26.  
    27. ?>
    Что делать со знаками пунктуации, решите сами.
     
  5. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    Dima4321
    PHP:
    1. <?php
    2.      header("Content-type:text/plain");
    3.      $new   = 'hello my friend, hello my friend,hello,goodbuy my friend,hello my friend,goodbuy my friend,goodbuy, hello my friend,';
    4.          $words = array('hello','my');
    5.      $new   = explode(' ',str_replace(',',' ',$new));  //лучше бы все лишние знаки убрать
    6.      $new   = array_intersect($new,$words);
    7.      print_r($new);
    8. ?>
    дальше догадаешься что делать?
    для подбора фраз из текста есть алгоритм цепей маркова
     
  6. Dima4321

    Dima4321 Активный пользователь

    С нами с:
    1 апр 2009
    Сообщения:
    683
    Симпатии:
    0
    Padaboo Спасибо...
    боюсь показаться валенком , но ты точно понял пою задачу ??


    массив $words с кол-ом вхождений например больше 2 одинаковых слов я получу с помощью регулярки

    1) \b.+?\b

    2) и функции $new=array_count_values($match[0]);

    3) выеду массивом где кол-о одинкаовых слов будет больше двух

    foreach($new as $key=>$value)
    {
    if($value>2 and strlen($key)>2)
    {
    echo $key. '---'.$value.'<br>';
    }
    }

    Дальше по твоей логике я буду сравнивать слова со словами в массивах...а смысл ???

    Мне нужны четкие фразы !!! Кол-во вхождений одинаковых слов я и так узнал с помощью пункта 1 и 2.

    Мне нужно кол-во вхождений фраз. Фраз текст которых я изначально не знаю.

    Я валенок ??? только честно ??))
     
  7. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    Dima4321
    хз, из моего массива если лишний my исключить получится количество (5) идущих друг за другом фраз hello * my
    hello my
    hello my
    hello,goodbuy my
    hello my
    hello my
     
  8. Dima4321

    Dima4321 Активный пользователь

    С нами с:
    1 апр 2009
    Сообщения:
    683
    Симпатии:
    0
    Хотя появилась идея, что можно одинаковые слова пустить снова в текст и помещать в массив ближайшие слова включительно с этим словом.

    т.е. нашли что существуют одинаковые слова с хорошей плотностью

    hello

    my

    friend

    пустили их через preg_match_all и вывели эти слова уже как фразу( т.е. слово поиска захватило ближайшее слово) :

    hello
    my

    friend
    hello


    Потом останется лишь выбрать массивы фраз с большим кол-ом вхождений .
     
  9. Apple

    Apple Активный пользователь

    С нами с:
    13 янв 2007
    Сообщения:
    4.984
    Симпатии:
    2
    Куча решений, а если посмотреть на задачу?
    Сделаем сферическую строку в вакууме:

    1. Имеется строка, в которой n слов.
    2. Строка представляет из себя набор конечный символов [a-zA-Z0-9-], всё остальное - разделители
    3. Требуется найти количество одинаковых фраз, где фраза - это количество элементов типа "слово"

    4. Разбиваем строку на фразы, где фраза содержит <= 2 слова. Всего у нас выйдет строк n/2 при условии, если n - четное, и (n/2 - 1) если n - нечетное.

    5. Сравнение даст положительный результат только в том случае, если количество слов между конечными фразами будет равно количеству слов во фразе, т.е

    мир, река, мир, мир, мир, река (шаг=2): (мир-река, мир-мир, мир-река). Если в этом множестве извлечь третий элемент, то все подсчеты логически рассыпятся.
     
  10. Jampire

    Jampire Активный пользователь

    С нами с:
    22 авг 2009
    Сообщения:
    181
    Симпатии:
    0
    Адрес:
    Гомель
    Нет, вы просто не умеет читать форум. Сообщение №4:
    Код (Text):
    1. Array
    2. (
    3.     [hello my] => 4
    4.     [my friend,] => 6
    5.     [friend, hello] => 1
    6.     [friend,hello,] => 1
    7.     [hello,goodbuy] => 1
    8.     [goodbuy my] => 2
    9.     [friend,hello] => 1
    10.     [friend,goodbuy] => 2
    11.     [friend,goodbuy,] => 1
    12.     [goodbuy, hello] => 1
    13.     [friend,] => 1
    14. )
     
  11. Jampire

    Jampire Активный пользователь

    С нами с:
    22 авг 2009
    Сообщения:
    181
    Симпатии:
    0
    Адрес:
    Гомель
    Код (Text):
    1. world, river, world, world, world, river
    2. Array
    3. (
    4.     [world, river,] => 1
    5.     [river, world,] => 1
    6.     [world, world,] => 1
    7.     [world, river] => 1
    8.     [river] => 1
    9. )
    Код (Text):
    1. world, river, world, world, river
    2. Array
    3. (
    4.     [world, river,] => 1
    5.     [river, world,] => 1
    6.     [world, world,] => 1
    7.     [world, river] => 1
    8.     [river] => 1
    9. )
    Код можно легко доработать, чтобы пропускать разделители.
     
  12. Apple

    Apple Активный пользователь

    С нами с:
    13 янв 2007
    Сообщения:
    4.984
    Симпатии:
    2
    Jampire
    Опиши словами алгоритм, мне лень разбираться в коде =)
     
  13. Dima4321

    Dima4321 Активный пользователь

    С нами с:
    1 апр 2009
    Сообщения:
    683
    Симпатии:
    0

    А мне даже не все понятно..но здорово..как некрутил вставлял фразы в середину ..все равно ищет четко совпадения..

    сам придумал или подглядел..опиши принцип ???))

    Вот видишь Apple есть еще светлые головы. Главное желание !
     
  14. Jampire

    Jampire Активный пользователь

    С нами с:
    22 авг 2009
    Сообщения:
    181
    Симпатии:
    0
    Адрес:
    Гомель
    Доработка на случай разделителей:
    PHP:
    1. <?php
    2.  
    3. function prepare_phrase($string, $step = 2) {
    4.     if ($step <= 0)  {
    5.         die("Step must be greater than 0!");
    6.     }
    7.     $string = preg_replace('/\W+/s', " ", $string);
    8.     $string = trim(preg_replace('/\s+/s', " ", $string));
    9.     return count_phrase($string, $step);
    10.  
    11. }
    12.  
    13. function count_phrase($string, $step)   {
    14.     global $result;
    15.     if (preg_match_all('/\w+/s', $string, $pockets) == $step - 1)  {
    16.         return $result;
    17.     }
    18.     preg_match('/(\s?\w+\s?){'.$step.'}/s', $string, $matches);
    19.     $matches[0] = trim($matches[0]);
    20.     if (!isset($result[$matches[0]]))   {
    21.         $result[$matches[0]] = substr_count($string, $matches[0]);
    22.     }
    23.     $string = preg_replace('/^\w+\s?/s', "", $string);
    24.     count_phrase($string, $step);
    25. }
    26.  
    27. $result = array();
    28. $str = "hello my friend, hello my friend,hello,goodbuy my friend,hello my friend,goodbuy my friend,goodbuy, hello my friend,";
    29. //$str = "world, river, world, world, river";
    30. //$str = "world, river, world, world, world, river";
    31. echo $str."<br />";
    32. prepare_phrase($str, 2);
    33. print_r($result);
    34.  
    35. ?>
    Простая рекурсия. В функции prepare_phrase() подготавливаем строку: все разделители заменяем на одиночные пробелы.Подготовленную строку передаем в рекурсионную функцию count_phrase(). В функции при помощи регулярки находим первые два слова (фраза). Считаем количество совпадений этой фразы во всей строке. Удаляем первое слово в строке и передаем новую строку в рекурсию. Так работаем пока в строке не останется одно слово. Когда останется одно слово - останов рекурсии и возврат результирующего массива. Результирующий массив: ключи есть фразы, значения есть число совпадений.

    Наблюдается баг с функцией substr_count:
    PHP:
    1. <?php
    2.  
    3. $str = "world world world";
    4. echo substr_count($str, "world world").;
    5.  
    6. ?>
    Результат 1, а должно быть 2.
     
  15. Dima4321

    Dima4321 Активный пользователь

    С нами с:
    1 апр 2009
    Сообщения:
    683
    Симпатии:
    0
    Мое решение свелось к этому:

    PHP:
    1. preg_match_all('/\b.+?\b/si',$new,$match);
    2.  
    3. $n=array_count_values($match[0]);
    4.  
    5. arsort($n);
    6.  
    7.  
    8. foreach($n as $key=>$value)
    9. {
    10. if($value>7 and strlen($key)>=4)
    11. {
    12. preg_match_all("/$key\s\b.+?\b/si",$new,$match);
    13. foreach($match[0] as $value)
    14. {
    15. echo $value;
    16. echo '<br>';
    17.  
    18. }
    19. }
    20. }
    так же замечу , что код работает не сликом корректно на больших текстах и почему-то упускает несколько слов.

    и кстати вот такой кусок почему-то не работает вовсе

    PHP:
    1. foreach($n as $key=>$value)
    2. {
    3. if($value>7 and strlen($key)>=4)
    4. {
    5. preg_match_all("/\b.+?\b\s$key/si",$new,$m);
    6. foreach($m[0] as $value)
    7. {
    8. echo $value;
    9. echo '<br>';
    10.  
    11. }
    12. }
    13. }
    захватывает весь текст
     
  16. Dima4321

    Dima4321 Активный пользователь

    С нами с:
    1 апр 2009
    Сообщения:
    683
    Симпатии:
    0
    И вот еще:

    http://www.seotxt.com/service/optimizer/

    Эта штука все таки работает корректно !! Интересно у нее такой же код как и уJampire )) ??
     
  17. Dima4321

    Dima4321 Активный пользователь

    С нами с:
    1 апр 2009
    Сообщения:
    683
    Симпатии:
    0
    Вот упрощенный мой код. Вроде работает как надо. Попробуйте сделать тест из простой строки.

    Мне даже не верится что я сделал что-то похожее на анализатор..)) Не усну.

    первый массив вхождение двух слов
    второй массив вхождение одного слова.

    PHP:
    1. <?
    2. ini_set('display_errors',1);
    3.  error_reporting(E_ALL);
    4.  
    5. $new='derevo staroe derevo hello my hello my hello my fedor misha  hello my hello dima hello my staroe derevo hello dima hello dima staroe derevo ';
    6.  
    7. echo $new.'<br>';
    8.  
    9.  
    10. preg_match_all('/\b.+?\b/si',$new,$match);
    11.  
    12. $n=array_count_values($match[0]);
    13.  
    14. arsort($n);
    15.  
    16. $r="";
    17.  
    18. foreach($n as $key=>$value)
    19. {
    20. if($value>=3 and strlen($key)>1)
    21. {
    22. preg_match_all("/$key\s\b.+?\b/si",$new,$match);
    23. foreach($match[0] as $value)
    24. {
    25. $r .=$value.'<br>';
    26. }
    27. }
    28. }
    29. $rr=preg_split('/<br>/',$r);
    30. arsort($v);
    31. echo '<pre>';
    32. print_r($v);
    33. echo '</pre>';
    34.  
    35.  
    36. echo '<pre>';
    37. print_r($n);
    38. echo '<pre>';
    39.  
    40.  
    41.  
    42.  ?>
    43.  
     
  18. <?=RPG?>

    <?=RPG?> Активный пользователь

    С нами с:
    19 ноя 2010
    Сообщения:
    451
    Симпатии:
    0
    Когда-то давно я решал такую задачу, когда хотел найти в своей статье часто повторяющиеся выражения. Для этого использовал регулярные выражения. Проблема в том, что это выражение ищет повторы слов и фраз из любого количества слов. Но думаю под свои нужды переделаете.

    PHP:
    1. <?php
    2. $text = 'derevo staroe derevo hello my hello my hello my fedor misha  hello my hello dima hello my staroe derevo hello dima hello dima staroe derevo';
    3. preg_match_all('/(?=(\b\w+(?:\s+\w+)*\b).*?\1)/', $text, $k);
    4. $words = array_unique($k[1]);
    5. $res = array();
    6. foreach($words as $v)
    7.   $res[$v] = substr_count($text, $v);
    8. arsort($res);
    9. print_r($res);