Russian Qt Forum
Май 07, 2024, 01:36 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 2 [3] 4 5 ... 18   Вниз
  Печать  
Автор Тема: Регулярное выражение с QString  (Прочитано 152103 раз)
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #30 : Июль 25, 2013, 18:00 »

Спасибо.
Вторая часть совета как раз и есть метод проб и ошибок. Забавно то, что большинство тех кто отрицает метод проб и ошибок, как раз в первую отправляют заниматься этим делом Улыбающийся, причем в худшем проявлении этого метода.
Но про метод проб и ошибок я не в буквальном смысле писал. Метод научного тыка - общепризнанный метод вообще как таковой. Иначе бы не имели того, что сейчас имеем.
Ох в очередной раз пустились во флуд Улыбающийся.

А никто и не говорит, что метод проб и ошибок - это зло)
Печально, когда процесс познания слепо основывается только на нём( И игнорируется опыт и знания накопленные до этого( 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4727



Просмотр профиля WWW
« Ответ #31 : Июль 25, 2013, 18:48 »

делай trimmed для исходной строки
Типа этого:
Код
C++ (Qt)
   QClipboard *clipboard = QApplication::clipboard();
   QString xt = clipboard->text();
   QString originalText = xt.trimmed();
Не сработало.

ну значит проблема в чем-то другом
Проблема решена. Покритикуйте:
я лично вообще не вижу разницы с предыдущим кодом, разве что только если начну построчно сравнивать.

общая критика кода: он ужасен т.к. просто читая его очень трудно понять что там происходит (во всяком случае мне).

2m_ax: бессмысленно сотрясаешь воздух. Spark будет делать так, как ему хочется (методом научного тыка), а не так, как хорошо, в чем можно было убедиться по его предыдущим задачам и решениям.

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

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
Spark
Гость
« Ответ #32 : Июль 25, 2013, 19:16 »

Парни! Я ведь выкладываю обычно готовый код и как правило рабочий. Неужели вы думаете, что это можно делать основываясь только на методе проб и ошибок? Естественно пользуюсь справочниками, книгами, видеоматериалом, вас спрашиваю, пользуюсь поиском и изучаю чужой код, кто как решал подобное. Но в любом случае на долю проб и ошибок уходит львиная доля работы. По моему достаточно серьезные задачи перед собой ставлю после двух месяцев обучения. Я слабый ученик? Ну что ж какой есть, остальные наверное после родов сразу на двух языках говорить умели.

я лично вообще не вижу разницы с предыдущим кодом, разве что только если начну построчно сравнивать.
В общем то подписал:
Код
C++ (Qt)
           trimmedStr = itemStr.trimmed();
//  Убираем пустышку
           if( trimmedStr.isEmpty() )
               continue;

Код ужасен. Ну не слово в слово, не буква в букву, но ведь работает Улыбающийся.
Однако с точки зрения техники исполнения предложений больше не было, кроме того варианта, что раскритиковали. Сделал только с учетом ваших поправок. Значит достаточно нормально. Остальное буду править по мере осознания как можно лучше.
Записан
Spark
Гость
« Ответ #33 : Июль 26, 2013, 05:15 »

Необходимо разделить частотный список по языкам. Английский текст не сложно удалить:
Код
C++ (Qt)
   QClipboard *clipboard = QApplication::clipboard();
   QString originalText = clipboard->text();
 
   originalText = originalText.toLower();
   originalText.replace(QRegExp("[a-z]"), "");
Русский не удается удалить:
Код
C++ (Qt)
   originalText.replace(QRegExp("[а-я]"), "");
Как поступить?
Записан
Majestio
Гость
« Ответ #34 : Июль 26, 2013, 06:19 »

Смешались в кучу кони, люди, и залпы тысячи орудий слились в протяжный вой (С)

Уважаемый топикстартер, задумываясь о реализации, вы бы сперва удосужились выполнить два важных шага:

1) постановка задачи
2) описание алгоритма

Публикуя в первом сообщении вопрос, и приведя п.1-2, был бы разговор более предметным. Прокачивать телепатию - дело неблагодарное.

По сути вопроса...

Ответ одному из участников обсуждения: "Алгоритм в один проход более оптимален" - смелое, но в полном обобщении - бредовое утверждение. Все зависит от накладных расходов, требуемых для реализации алгоритма. Если придется для "рассовой чистоты" (один проход) строить Китайскую Стену, то я против.

Как я понял, смысл задачи - разбить текст на слова, подсчитать вхождения? Если я правильно понял, то QString::split - разбивать замечательно умеет. Таким образом, вам придется только определиться с набором символов-разделителей. Обратите внимание на документацию:

Код:
QString str;
QStringList list;

str = "Some  text\n\twith  strange whitespace.";
list = str.split(QRegExp("\\s+"));
// list: [ "Some", "text", "with", "strange", "whitespace." ]

В примере выше в качестве разделителей используются "пробельные" символы (пробел, табуляция, перевод строки, возврат каретки). Модифицируйте это под себя - добавьте знаки пунктуации ... и сделайте разбиение вашего текста одним оператором. Получите QStringList, содержащий весть набор слов из текста.

Далее пробегайте по списку и строите частоты, частоты имхо, удобно хранить в QMap<QString, int>. Таким образом код можно переписать в виде:

Код:
QString Text = tr("Это, входной: текст");
QStringList List = Text.split(QRegExp("\\s+"),QString::SkipEmptyParts);
QMap <QString, int> MyMap;
foreach (QString Item, List) MyMap[Item] = (MyMap.contains(Item)) ? MyMap[Item]+1 : 1;

Замечания:
1. Возможно QMap - не самое быстрое решение. Читаем тут. Эксперементируем.
2. Возможно последнюю строчку можно реализовать и так: foreach (QString Item, List) MyMap[Item]++; я не пробовал, не совсем помню все ньюансы работы с контейнерами

Вобщем как-то так.
« Последнее редактирование: Июль 26, 2013, 06:27 от Majestio » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #35 : Июль 26, 2013, 08:11 »

Ответ одному из участников обсуждения: "Алгоритм в один проход более оптимален" - смелое, но в полном обобщении - бредовое утверждение.
Что значит в "полном обобщении"? И почему оно бредовое? Улыбающийся

Все зависит от накладных расходов, требуемых для реализации алгоритма.
Накладные расходы минимальные, реализовать алгоритм в один проход очень просто. Поэтому, рекомендовать реализацию в два прохода просто глупо. Тем более повторно.


Записан
Majestio
Гость
« Ответ #36 : Июль 26, 2013, 08:48 »

Ответ одному из участников обсуждения: "Алгоритм в один проход более оптимален" - смелое, но в полном обобщении - бредовое утверждение.
Что значит в "полном обобщении"? И почему оно бредовое? Улыбающийся

Не все алгоритмы линейны. Значит утверждение - носит частный характер.
Бредовое - потому как сделано без оглядки на полную постановку задачи, которой, увы, так и не прозвучало.

Все зависит от накладных расходов, требуемых для реализации алгоритма.
Накладные расходы минимальные, реализовать алгоритм в один проход очень просто. Поэтому, рекомендовать реализацию в два прохода просто глупо. Тем более повторно.

Регекспы - мощнейшее средство обработки текста. А в Qt оно еще работает и с кодировками. Реализовать парсинг текста с помощью регэкспов - на порядок проще, нежели последовательным сканированием и вычленением. Следовательно изобретать велосипед (сканер) бессмысленно при наличии готовых удобных средств. В этом и смысл фреймворка - экономить время кодинга & code reuse.

Если чуть-чуть изменить постановку задачи, последовательный парсинг вырастет в монстра.

Пример с потолка - вычислить "веса" слов по формуле:

Вес_слова = количество повторений*суммарное_количество_слов_стоящих_после.

Попробуйте построить однопроходной алгоритм (рекурсию не предлагать, стек ограничен, работаем с кучей).
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #37 : Июль 26, 2013, 09:00 »

Не все алгоритмы линейны. Значит утверждение - носит частный характер.
Какое утверждение, причем здесь линейность алгоритмов?

Бредовое - потому как сделано без оглядки на полную постановку задачи, которой, увы, так и не прозвучало.
Какую полную постановку? ТС нужно посчитать частоты вхождения слов, это полная постановка задачи.

Вот этот поток сознания к чему вообще? Улыбающийся
Регекспы - мощнейшее средство обработки текста. А в Qt оно еще работает и с кодировками. Реализовать парсинг текста с помощью регэкспов - на порядок проще, нежели последовательным сканированием и вычленением. Следовательно изобретать велосипед (сканер) бессмысленно при наличии готовых удобных средств. В этом и смысл фреймворка - экономить время кодинга & code reuse.

Если чуть-чуть изменить постановку задачи, последовательный парсинг вырастет в монстра.

Пример с потолка - вычислить "веса" слов по формуле:

Вес_слова = количество повторений*суммарное_количество_слов_стоящих_после.

Попробуйте построить однопроходной алгоритм (рекурсию не предлагать, стек ограничен, работаем с кучей).
Нам нужно решить конкретную задачу, ее легко можно решить в один проход, причем совсем без регулярных выражений. Все. Улыбающийся
Записан
Majestio
Гость
« Ответ #38 : Июль 26, 2013, 09:19 »

Какую полную постановку? ТС нужно посчитать частоты вхождения слов, это полная постановка задачи.
Нам нужно решить конкретную задачу, ее легко можно решить в один проход, причем совсем без регулярных выражений. Все. Улыбающийся

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

Постановка задачи

Входной текст - латинские символы алфавита+русские, кодировка UTF8. Разделителями считаем - пробельные символы (пробел, табуляция, перевод строки, возврат каретки), точку, запятую, двоеточие, точка с запятой, тире в виде минуса, открывающую и закрывающую скобки.

Моя "неоптимальная реализация":

Код:
QTextCodec *Сodec = QTextCodec::codecForName("UTF-8");
QTextCodec::setCodecForLocale(Сodec);
QString Text = tr("Это, входной: текст");
QStringList List = Text.split(QRegExp("[\\s\.,:;-()]+"),QString::SkipEmptyParts);
QMap <QString, int> MyMap;
foreach (QString Item, List) MyMap[Item] = (MyMap.contains(Item)) ? MyMap[Item]+1 : 1;

Покажите ваш оптимальный код. Без кораблей, бороздящих океаны ...
Записан
Spark
Гость
« Ответ #39 : Июль 26, 2013, 09:46 »

Итак задача. Взять текст из буфера и составить частотный список:
- Всего слов
- Количество вхождений каждого слова
- Сортировка

Значит в рамках QT и данной функции (bufferButtonSave()) решение никому не нравится?:
Код
C++ (Qt)
void MainWindow::bufferButtonSave()
{
   QClipboard *clipboard = QApplication::clipboard();
   QString originalText = clipboard->text();
   originalText = originalText.toLower();
   originalText.replace(QRegExp("\\W"), " ");
   originalText.replace(QRegExp("\\d"), " ");
 
   // ЭТО НЕ РАБОТАЕТ
   originalText.replace(QRegExp("[а-я]"), "");
 
       QTextStream fileStream( & originalText );
       QString itemStr, trimmedStr;
       QStringList itemList;
       QString countItemString;
       QString fullCountItemString;
 
       int fullCountItem(0);
 
       textory.clear();
 
       do
       {
           // Формируем список пословно
           fileStream >>(itemStr);
 
           trimmedStr = itemStr.trimmed();
           if( trimmedStr.isEmpty() )
               continue;
 
           QRegExp rx("\\b(" + trimmedStr + ")\\b", Qt::CaseInsensitive);
 
           int countItem(0);
           int pos(0);
 
           while ((pos = rx.indexIn(originalText, pos)) != -1)
           {
               ++countItem;
               ++pos;
           }
 
           countItemString.setNum(countItem);
           ++fullCountItem;
 
           trimmedStr = trimmedStr + "\t" + countItemString;
           itemList.prepend( trimmedStr );
 
       } while( !fileStream.atEnd() );
 
       // Выводим список на экран:
 
       textory.enableAdd( true );
 
       fullCountItemString.setNum(fullCountItem);
 
       // СОРТИРУЕМ: РАБОТАЕТ В ПОРЯДКЕ ВОЗРАСТАНИЯ.
       // НАДО1 - В ПОРЯДКЕ УБЫВАНИЯ.
       // НАДО2 - ОТСОРТИРОВАТЬ В ПОРЯДКЕ ЧАСТОТНОСТИ.
       qSort(itemList);
 
       foreach (QString itm, itemList)textory.addItem( Textory::Item( 1, itm ) );
 
       // Заголовок спсика:
ui.textoryWidget->getTextoryLabel().setText( "Frequency list (" + fullCountItemString + ")" );
}
Получаем вроде этого:

Всеобщее мнение можно сделать более быстрый, надежный алгоритм? Если так, буду думать.
В алгоритме два комментария в верхнем регистре - задачи над которыми сейчас работаю. Если кто захочет помочь, ничего не имею против Улыбающийся.
« Последнее редактирование: Июль 26, 2013, 10:02 от Spark » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #40 : Июль 26, 2013, 10:02 »

Проблема решена. Покритикуйте:
Ну пока плохо. m_ax совершенно прав - Вам не хватает размаха, ковыряетесь в мелочах вместо того чтобы видеть конструкцию в целом. Такой подход часто называют "ловлей блох".

Вы добавляете частоту как строку, т.е. просто показываете ее юзеру. Это не по-хозяйски, результаты расчета надо хранить чтобы потом не бегать. Не нужно даже тратить время на размышления типа "а где еще она может понадобиться".

Если Вы упоминаете о скорости - то зачем Вы делаете квадратичный алгоритм? Ведь просмотр всей исходной строки (потенциально большой) для каждого слова - это просто глупость. Гораздо быстрее и проще сначала получить слова и оперировать уже с ними.

Смешивание UI с расчетами выгдядит совершенно безобразно. К простой истине "борщ отдельно, мухи отдельно" можно было прийти за время пребывания на форуме - вместо того чтобы ссылаться на недостаток опыта и.т.п.

Код ужасен. Ну не слово в слово, не буква в букву, но ведь работает Улыбающийся.
Однако с точки зрения техники исполнения предложений больше не было,
Неправда, были. Работать может и работает, но на последнем издыхании - запас прочности нулевой. Следующая простейшая задача русский/английский уже вызывет большие трудности. А было бы по уму, со словами - достаточно добавить всего одну ф-цию, напр GetWordLang(). Вместо этого опять чего-то риплейсите с тем regexp который здесь ни пришей - ни пристегни.
Записан
Spark
Гость
« Ответ #41 : Июль 26, 2013, 10:16 »

Результаты расчета хранятся в текстовом файле. Формат удобен для Excel:
Слово табуляция частота.
Информацию сложно вытащить в случае необходимости?

По поводу недостатка опыта. Однако очевидно, что что бы он пришел недостаточно пары месяцев. Вы считаете, что я решаю тривиальные задачи для новичка? По поводу UI и расчетов. Меня интересует реализация самой функции. Организационные вопросы меня не интересуют. Со временем само придет.

Ok! Попробую разобраться с другим алгоритмом.
« Последнее редактирование: Июль 26, 2013, 10:24 от Spark » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #42 : Июль 26, 2013, 11:03 »

Результаты расчета хранятся в текстовом файле ...
Не спорьте и не пререкайтесь со старшими. Вы всегда можете "пойти своим путем", эта возможность никуда не убежит  Улыбающийся

Сортируем по частоте (по убыванию)

Код
C++ (Qt)
struct CFreq {
CFreq( int count = 1,  const QString * str = 0 ) : mCount(count), mStr(str) {}
bool operator < ( const CFreq & sec ) const { return mCount > sec.mCount; }
 
int mCount;
const QString * mStr;
};
 
typedef QMap <QString. int> TMapStr;
TMapStr theMap;
... // заполняем мапу - см выше
 
// заполняем вектор
QVector <CFreq> theFreq;
for (TMapStr::const_iterator it = theMap.begin(); it != theMp.end(); ++it)
theFreq.push_back(CFreq(it->value(), &it->key()));
 
// сортируем
qSort(theFreq.begin(), theFreq.end());
 
// печатаем
for (int i = 0; i < theFreq.size(); ++i)
qDebug() << *theFreq[i].mStr << "\n"
 
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #43 : Июль 26, 2013, 11:31 »

Покажите ваш оптимальный код. Без кораблей, бороздящих океаны ...
То, что набросал за 5 минут:
Код
C++ (Qt)
void stat()
{
QString src = "erwerwer ewr werwerwerwerew werwerwe sdfsdfsdf tyutyu\tdsfsdfds+werewrer-ewr";
QString sep = "[ \\t\\n\\r+-]";
QHash<QString, int> hash;
 
QRegExp rx( sep );
 
int start = 0;
int cur = 0;
for(;;)
{
cur = rx.indexIn( src, cur );
QString word = src.mid( start, (cur != -1)? cur - start : -1 );
if( !word.isEmpty() )
{
qDebug() << word;
hash[ word ]++;
}
 
if( cur == -1 )
break;
 
start = ++cur;
}
 
qDebug() << hash;
}
 
Сравните пока с этим, а если будет нужно, я уберу регулярку и тогда вы удивитесь еще больше. Улыбающийся
« Последнее редактирование: Июль 26, 2013, 13:04 от Old » Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #44 : Июль 26, 2013, 13:50 »

Я бы всё же написал отдельный класс для этого дела.

И ещё по поводу сплита (QString::split) имхо, это немного не то, когда речь идёт о статистике слов. Сплит задаёт правила для разделителей, а не для самих токенов. В связи с этим split выглядит несколько ущербным по своим возможностям для данной постановки вопроса. Что если я хочу предоставить пользователю следить за статистикой отдельных букв, или слов или только цифр или..

Не знаю как в Qt, но для такой цели в boost'е есть очень удобный regex_iterator (я о том, который входит именно в boost::xpressive, а не в boost::regex)
А одна из фишек xpressive - можно легко задавать варианты поиска с учётом и без учёта регистра. (в boost::regex не нашёл как это можно легко сделать)
  
Короче, я бы накидал такой классик (word_statistics):

Код
C++ (Qt)
#include <iostream>
#include <boost/xpressive/xpressive.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <string>
#include <unordered_map>
 
class word_statistics
{
public:
   typedef std::string string_type;
   typedef std::unordered_map<string_type, unsigned> hash_type;
   typedef typename hash_type::size_type size_type;
   typedef typename hash_type::const_iterator const_iterator;
 
   word_statistics(const string_type & source) : m_source(source), m_total_count(0) {}
 
   void compile(const string_type & expr, bool icase = false) {
       using namespace boost::xpressive;
       sregex e = (icase) ? sregex::compile(expr, regex_constants::icase) : sregex::compile(expr);
       sregex_iterator it(m_source.begin(), m_source.end(), e);
       sregex_iterator end;
       m_hash.clear();
       for (m_total_count = 0; it != end; ++it, ++m_total_count) {
           if (icase)
               ++m_hash[boost::algorithm::to_lower_copy(it->str(0))];
           else
               ++m_hash[it->str(0)];
       }
   }
 
   size_type total_count() const { return m_total_count; }
   size_type size() const { return m_hash.size(); }
   const_iterator begin() const { return m_hash.begin(); }
   const_iterator end() const { return m_hash.end(); }
 
private:
   string_type m_source;
   size_type m_total_count;
   hash_type m_hash;
};
 
int main()
{
 
   std::string str("text1, text2 and a lot of TextT 3");
   word_statistics word_stat(str);
 
   word_stat.compile("(\\d+)", true);
   std::cout << "only digit:\n";
   for (auto s : word_stat) {
       std::cout << s.first << " = " << s.second << std::endl;
   }
   std::cout << "total count = " << word_stat.total_count() << "\n\n";
 
   std::cout << "only word:\n";
   word_stat.compile("([a-zA-Z]+)", false);
   for (auto s : word_stat) {
       std::cout << s.first << " = " << s.second << std::endl;
   }
   std::cout << "total count = " << word_stat.total_count() << "\n\n";
 
   std::cout << "only [a e t]:\n";
   word_stat.compile("([aet])", true);
   for (auto s : word_stat) {
       std::cout << s.first << " = " << s.second << std::endl;
   }
   std::cout << "total count = " << word_stat.total_count() << "\n\n";
 
   return 0;
}
 


и результаты будут такие:
Код
Bash
source = text1, text2 and a lot of TextT 3
 
only digit:
1 = 1
2 = 1
3 = 1
total count = 3
 
only word:
TextT = 1
of = 1
lot = 1
a = 1
and = 1
text = 2
total count = 7
 
only [a e t]:
e = 3
a = 2
t = 8
total count = 13
 

Да, ну ещё бы добавил возможность задавать сортировку и установку локали..
« Последнее редактирование: Июль 26, 2013, 14:10 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Страниц: 1 2 [3] 4 5 ... 18   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.219 секунд. Запросов: 22.