Универсальный код деления на предложения...

Тема в разделе "PHP", создана пользователем System777, 15 сен 2008.

Статус темы:
Закрыта.
Модераторы: latteo
  1. System777

    System777 Создатель

    Регистр.:
    20 апр 2008
    Сообщения:
    30
    Симпатии:
    13
    Подкиньте, пожалуйста, если у кого есть универсальный код для деления любого (русского, но возможно с англ. терминами) текста на предложения.

    Важен код для всех возможных случаев предложений:

    1. Обычное предложение: Слово слово слово слово.

    2. Восклицательные, вопросительные, с "...": Слово слово!

    3. С встречающимися числами с точкой: Слово 22.1 слово.

    4. С прямой речью: Слово сказал: "Это слово слово". и также Слово сказал: "Это слово слово?" (вопросительные - знак перед кавычкой).

    5. В виде диалога:

    - Привет, - сказал Вася.

    6. Сложные и смешанные с терминами:

    - "Это слово 22$ domain", - кто-то сказал (из наших), - "нужно понимать №22.33 правильно в Магадане!"

    7. С учётом сокращений: Я живу в г. Москва!

    8. В идеале - ещё бы определять предложения, где вместо символа конца ставят смайл :) или ещё что-то

    Например:

    Привет : ) Как дела?

    Возможны и другие варианты, может я что-то не учёл...

    Отдельные предложения потом будут дополнительно анализироваться, но важно чтобы они были отделены.

    Заранее благодарю!
     
    Dador нравится это.
  2. RolCom

    RolCom

    Регистр.:
    12 мар 2008
    Сообщения:
    351
    Симпатии:
    108
    Как ты предполагаешь искать сокращения? 100% нельзя различить, где сокращение, а где конец предложения.
     
  3. System777

    System777 Создатель

    Регистр.:
    20 апр 2008
    Сообщения:
    30
    Симпатии:
    13
    Ну это, в принципе, не критично, таких исходных текстов (с сокращениями) думаю, будет не так много. Остальные пункты важнее...

    Также, как вариант, можно просто составить базу популярных сокращений - и потом либо определять, либо делать автозамену ;)
     
  4. bookinist

    bookinist Создатель

    Регистр.:
    15 фев 2007
    Сообщения:
    43
    Симпатии:
    12
  5. RolCom

    RolCom

    Регистр.:
    12 мар 2008
    Сообщения:
    351
    Симпатии:
    108
    Прикольно , хотя и не без глюков. 'я' на конце предложения он считает за сокращение, например:
    Привет, это я. Как дела?
    посчитает за одно предложение. За одно предложение посчитает также:
    Это город Москва.
    Это памятник Ленину.

    А еще могли бы отсекать неизвестные сокращения не в конце предоложения, если следующее слово с прописной буквы.
     
  6. bookinist

    bookinist Создатель

    Регистр.:
    15 фев 2007
    Сообщения:
    43
    Симпатии:
    12

    У них же написано в TextParser.dot-reductions.txt
    "#однобуквенные сокращения уже пропускаются!"
    нужно порыться в коде, поискать где можно вставить исключения типа "я." :(

    а вот с Москвой я не понял
     
  7. RolCom

    RolCom

    Регистр.:
    12 мар 2008
    Сообщения:
    351
    Симпатии:
    108
    Код от меня. Перловый.
    Ток по смайлам пока не разбивает.
    В отличии от парсера выше:
    Прямую речь в кавычках и речь в скобках не разбивает.
    "Ну что это такое!!! Как так можно???" - возмутился я
    Однобуквенные сокращения пропускает, но букву 'я' учитывает, по ней разбивает.
    Привет, это я. Как дела?
    Если после сокращения слово начинается не с большой буквы, учитывает его. В противном случае проверяет слово по массиву.
    Это сокр. от слова
    Аналогично пропускает, если после многоточия нет заглавной буквы.
    С осенних дубов облетает... цена!
    Еще разбивает, если в конце предложения нет точки, но следующее начинается с новой строки и большой буквы (Хотя это может неправильно разбить если после переноса строки идет имя собственное, но таких случаев имхо куда меньше)
    Тестим:)

    Код:
    #!/usr/bin/perl
    use locale;
    use POSIX qw(locale_h);
    setlocale(LC_CTYPE,"ru_RU.CP1251");
    
    $text=<<EOF;         #текст
    Превед медвед
    EOF
    
    @sokr=('гр','бр');          #масив с сокращениями
    $i=0;
    #Строки формата Windows и Mac OS к формату Unix
    $text=~s/\r\n/\n/;
    $text=~s/\r/\n/;
    
    @predl=$text=~m{(
    \G(?:
        ([а-яa-z]++)|
        [^.\n?!а-яa-z\d("]++(?:[!\?]++)?|
        \.(?:\.*+)
           [^а-яa-z.?!\n]*+(?:\n[^а-яa-z.?!\n]*+)?
           (?=(?-i:[а-яa-z]))|
        (?<=\b[а-юa-z])\.|
        \.(?!\.)(?(?{while($i<@sokr && $sokr[$i] ne $2) {$i++;}; @sokr!=$i})|(?!))|
        \n[^а-яa-z.?!\n]*+(?=(?-i:[а-яa-z]))|
        \d++(?:\.\d++)?|
        (?:"(?=[^\s.]).*?(?<=[^\s])"|")|
        (?:\((?=[^\s.]).*?(?<=[^\s])\)|\( )
    )+
    [?!\n.]*
    )}isxg;
    
    
    
    if ($+[0]!=length($text)) {
       print "Ошибка разбора:\n";
       print $predl[-1],"[!]";
       print substr($text,$+[0], 20), "\n";
       print "-----------";
    }
    
    for ($i=0;$i<@predl;$i+=2) {
      $predl[$i]=~s/\n//;
      $predl[$i]=~s/^\s+//;
      $predl[$i]=~s/\s+$//;
      print $predl[$i],"\n";
    }
    
     
    tostrss, System777 и bookinist нравится это.
  8. myphpcoder

    myphpcoder Писатель

    Регистр.:
    20 окт 2008
    Сообщения:
    9
    Симпатии:
    1
    Хотел адаптировать эти регулярные выражения для php, что то пока без успешно. Но могли бы помочь немножко
     
  9. RolCom

    RolCom

    Регистр.:
    12 мар 2008
    Сообщения:
    351
    Симпатии:
    108
    Напрямую не получится, т.к. в выражении используется встроенный код, а в PHP такой фитчи нет.
     
  10. RolCom

    RolCom

    Регистр.:
    12 мар 2008
    Сообщения:
    351
    Симпатии:
    108
    myphpcoder
    По поводу PHP
    Решение "в лоб", одной регуляркой
    PHP:
    $sokr='\b'.join('.|\b',$sokr).'.';
    $re'
    {
    \G(?:
        [а-яa-z]++|
        [^.\n?!а-яa-z\d("]++(?:[!\?]++)?|
        \.(?:\.*+)
           [^а-яa-z.?!\n]*+(?:\n[^а-яa-z.?!\n]*+)?
           (?=(?-i:[а-яa-z]))|
        (?<=\b[а-юa-z])\.|
        \.(?!\.)(?<='
    .$sokr.')|
        \n[^а-яa-z.?!\n]*+(?=(?-i:[а-яa-z]))|
        \d++(?:\.\d++)?|
        (?:("(?=[^\s.]).*?(?<=[^\s])"|"))|
        (?:\((?=[^\s.]).*?(?<=[^\s])\)|\( )
    )+
    [?!\n.]*
    }isx'
    ;
    Но при большой базе сокрашений это будет медленно.
    Тогда, чтобы как в исходном примере, пробивать слово по массиву отдельно:
    PHP:
    $re='
    {
    \G(?:
        ([а-яa-z]++)|
        [^.\n?!а-яa-z\d("]++(?:[!\?]++)?|
        \.(?:\.*+)
           [^а-яa-z.?!\n]*+(?:\n[^а-яa-z.?!\n]*+)?
           (?=(?-i:[а-яa-z]))|
        (?<=\b[а-юa-z])\.|
        \n[^а-яa-z.?!\n]*+(?=(?-i:[а-яa-z]))|
        \d++(?:\.\d++)?|
        (?:"(?=[^\s.]).*?(?<=[^\s])"|")|
        (?:\((?=[^\s.]).*?(?<=[^\s])\)|\( )
    )+
    [?!\n.]*
    }isx'


    $i=0
    $s=false;
    while (
    preg_match($re$text$mPREG_OFFSET_CAPTURE$i)) {
       if(
    $s) {
          
    $predl[count($predl)-1].=$m[0][0];
       } else {
          
    $predl[]=$m[0][0];
       }
       
    $i=$m[0][1]+strlen($m[0][0]);
       if (
    preg_match('/(?<=[^.]\.)\G(?!\.)/'$text$w0$i) && in_array($m[1][0], $sokr))  {
           
    $s=true;
       } else {
           
    $s=false;
       }  
    }
     
    bookinist и myphpcoder нравится это.
Статус темы:
Закрыта.