Russian Qt Forum

Qt => Общие вопросы => Тема начата: thechicho от Июнь 03, 2013, 14:04



Название: QRegExp greedy. Разбить текст на предложения
Отправлено: thechicho от Июнь 03, 2013, 14:04
rx.setPattern("(.+(?:\\b[^А-ЯA-Z0-9])?(?:<li>|</li>|\\!|\\?|;|\\.|$))");    
   
   
Код:
    QString str = "blablabla. blablabla3...aaaab!!!fads???ffds?asdfvxcv";
    QRegExp rx; rx.setMinimal(true);
    rx.setPattern("(.+(?:\\b[^А-ЯA-Z0-9])?(?:<li>|</li>|\\!|\\?|;|\\.|$))");
    int pos = 0;
    while ((pos = rx.indexIn(str, pos)) != -1) {
        qDebug() << "rx.cap(0)" << rx.cap(0);
        QString tmp = rx.cap(1).remove(QRegExp("<[^>]*>")).trimmed();
        if (!tmp.isEmpty())
            proposals.append(tmp);
        pos += rx.matchedLength();
    }
выводит
Код:
rx.cap(0) "blablabla." 
rx.cap(0) " blablabla3."
rx.cap(0) ".."
rx.cap(0) "aaaab!"
rx.cap(0) "!!"
rx.cap(0) "fads?"
rx.cap(0) "??"
rx.cap(0) "ffds?"
rx.cap(0) "asdfvxcv"
а нужно
Код:
rx.cap(0) "blablabla." 
rx.cap(0) " blablabla3..."
rx.cap(0) "aaaab!!!"
rx.cap(0) "fads???"
rx.cap(0) "ffds?"
rx.cap(0) "asdfvxcv"

вопрос:
как сделать эту часть \\!|\\?|;|\\. жадной (greedy) так, чтобы часть .+ оставалась не жадной(non-greedy).
или есть другие варианты решения? может библиотеки готовые? нужно текст на предложения разбивать


Название: Re: QRegExp greedy. Разбить текст на предложения
Отправлено: tester64 от Июнь 03, 2013, 18:16
Можно использовать предваряющий поиск (?= или ?!).

Например:
Код
C++ (Qt)
QString s = "abc. abc123...abc!!!abc???abc?abc";
QMap<QString, QString> token({{"@cap", "A-Z0-9"}, {"@end", "!?;."}, {" ", ""}});
auto resolve = [&token] (const QString &s) -> QString { QString r(s); for (auto i = token.begin(); i != token.end(); ++i) r.replace(i.key(), i.value()); return r; };
QRegExp rx(resolve("\\S.* (?:\\b[^@cap])? (?:</?li> | [@end]+(?=[^@end]) | $)"));
rx.setMinimal(true);
for (int pos = 0; (pos = rx.indexIn(s, pos)) != -1; pos += rx.matchedLength())
   qDebug() << "  match" << rx.cap(0);

Здесь реализована простенькая подстановка (для наглядности регэкспа и избежания ошибок при дублировании подстрок) - так удобнее эспериментировать.
Кстати, смысл "(\\b[^А-ЯA-Z0-9])?" непонятен. С квантификатором ? это выражение будет просто игнорироваться. А без него - какой смысл запрещать конечные односимвольные слова в верхнем регистре?... И, если регистр не важен и не ставится целью исключить именно русский и латинский алфавиты, можно просто использовать [^\\w] или вообще \\W (в класс \w включаются произвольные алфавиты, подчерк, цифры и символы-модификаторы unicode классов Mn, Mc, Me).

ЗЫ. Код на C++11.
ЗЫ2. В коде удалена из регэкспа кириллица, у движка форума некорректный парсинг.


Название: Re: QRegExp greedy. Разбить текст на предложения
Отправлено: Bepec от Июнь 03, 2013, 18:59
Проще выражаясь средствами QRegExp никак) Он обрезанный.

Я пользуюсь deelx. Регэкспы в одном файле.


Название: Re: QRegExp greedy. Разбить текст на предложения
Отправлено: thechicho от Июнь 03, 2013, 20:04
спасибо за ответы!

решил проблему пока так. в принципе более менее универсально. пока устраивает
rxProposal.setPattern("(.+(?:\\b?[^А-ЯA-Z0-9]|\\B[А-ЯA-Z])(?:<li>.|</li>.|[\\!\\?\\.][^\\!\\?\\.]|$))");
Код:
QStringList proposals;
QRegExp rxProposal; rxProposal.setMinimal(true);
        rxProposal.setPattern("(.+(?:\\b?[^А-ЯA-Z0-9]|\\B[А-ЯA-Z])(?:<li>.|</li>.|[\\!\\?\\.][^\\!\\?\\.]|$))");

        int pos = 0;
        int proposalsTextLenght = proposalsText.length();
        while ((pos = rxProposal.indexIn(proposalsText, pos)) != -1) {
            qDebug() << "rxProposal.cap(0)" << rxProposal.cap(0);

            QString tmp = rxProposal.cap(1).remove(QRegExp("<[^>]*>"));           

            pos += rxProposal.matchedLength();
            if (pos != proposalsTextLenght) {
                pos -= 1;
                tmp.chop(1);
            }
            tmp = tmp.trimmed();
            if (!tmp.isEmpty())
                proposals.append(tmp);
        }

        qDebug() << endl << "proposals" << proposals.count();
        foreach (QString proposal, proposals) {
            qDebug() << proposal;
        }

"(\\b[^А-ЯA-Z0-9])?"
это я пытался сделать обработку
В.В. Путиным
(с 6.05.2013 по 15.05.2013)
глава США.

//Я пользуюсь deelx. Регэкспы в одном файле.
в чем преимущество по сравнению с QRegExp? не уверен, что deelx мне нужен.


Название: Re: QRegExp greedy. Разбить текст на предложения
Отправлено: Bepec от Июнь 03, 2013, 20:56
В полной поддержке рег экспов. И в отсутствии нелепых ограничений.


Название: Re: QRegExp greedy. Разбить текст на предложения
Отправлено: m_ax от Июнь 03, 2013, 21:02
Регулярки всё же немного для другого созданы..

Я бы смотрел в сторону boost::tokenizer. Правда там мы не узнаем символы конца предложения (?).

В готовых решениях уже приводилось два класса для таких целей.

Код
C++ (Qt)
#include <iostream>
#include "string_parser.h"
 
int main()
{
 
   std::string str = "abc. abc123...abc!!!abc???abc?abc.";
   string_parser<char> parser(".!?");
   size_t pos = 0;
   while ((pos = parser.next_token(str, pos, split_behavior::skip_empty_parts)) != string_parser<char>::npos()) {
       std::cout << string_parser<char>::trim(parser.token()) << parser.delimiter() << std::endl;
       pos += parser.matched_length();
   }
 
   return 0;
}
 


Вывод:
Код
Bash
abc.
abc123.
abc!
abc?
abc?
abc.
 


Название: Re: QRegExp greedy. Разбить текст на предложения
Отправлено: thechicho от Июнь 04, 2013, 11:03
//В полной поддержке рег экспов. И в отсутствии нелепых ограничений.
да вроде неплохая реализация. какие нелепые ограничения?

//Регулярки всё же немного для другого созданы..
для чего?

https://support.google.com/analytics/answer/1034324?hl=ru
Цитировать
Регулярными выражениями называют специальные символы, используемые для сравнения или извлечения фрагментов поля, а также правила, которыми регулируются все символы

нужен был такой вывод, с получением которого возникла сложность
Код:
abc.
abc123...
abc!!!
abc???
abc?
abc.

http://rakafon.blogspot.ru/2009/04/boosttokenizer.html
Цитировать
Использование boost::tokenizer
Нередко в практике программистов встречаются задачи, когда нужно разобрать строку (как еще иногда говорят - распарсить). Решений этой задачи - множество. Это и простейший поиск очередного символа-разделителя, и регулярные выражения, и специальные алгоритмы (например, из коллекции boost::string_algo). Но иногда логика разбиения строки на части (т. е. логика разбора) может быть ну совсем уж специфичная. Или регулярное выражение получается очень уж навороченное. Или по каким-то причинам библиотека регулярных выражений (тот же boost::regex) неприменима. Тогда на помощь может придти boost::tokenizer.
спасибо за совет, но судя по описанию, ограничусь регуляркой, т.к. мне не нужен идеальный результат.
достаточно, чтобы такого деления не было
6.05.2013
Код:
6.
05.
2013