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

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

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

Сообщений: 2095



Просмотр профиля
« : Апрель 20, 2014, 14:23 »

Недавно столкнулся с такой проблемой:

Мне нужно распарсить некоторый текст, формат которого предполагает наличие комментариев.
Комментарий начинается с определённого символа и заканчивается другим.
Всё что находится внутри закомментированого блока должно быть проигнорированно.
Комментарий может начинаться где угодно (не обязательно в начале строки)

Для парсинга использую boost::regex + boost::tokenizer..

Вопрос: Как наиболее логичнее организовать поддержку комментариев?
Метод "влоб" - это просто удалить из строки все комменты, и уже дальше парсить её.. Но этот вариант не очень(

Сейчас сделал так:
Написал свой bidirectional итератор, который при инкрементировании/декриментировании если встречает начало коммента, просто перескакивает его..
Подсовываю этот итератор в boost::regex и boost::tokenizer вместо std::string::const_iterator.. И всё работает)
Без существенного переписывания кода.. (спасибо гибкости boost)

Вот маленький пример: символ начала коммента - %, конец коммента - \n):
Код
C++ (Qt)
#include <iostream>
#include <boost/regex.hpp>
#include <string>
#include <list>
#include <boost/range/istream_range.hpp>
#include <algorithm>
 
template <class T = char>
class tex_string_iterator : public std::iterator<std::bidirectional_iterator_tag, T, ptrdiff_t, const T*, const T&>
{
public:
   typedef T char_type;
   typedef std::basic_string<T> string_type;
   typedef typename std::basic_string<T>::const_iterator string_iterator;
 
   tex_string_iterator(string_iterator begin, string_iterator end, T bra = '%', T ket = '\n')
       : _begin(begin), _end(end), _it(begin), _bra(bra), _ket(ket)
   {
       make_comments_list();
   }
 
   tex_string_iterator(const tex_string_iterator<T>& iter)
       : _begin(iter._begin), _end(iter._end), _it(iter._it),
         _bra(iter._bra), _ket(iter._ket), _comments_list(iter._comments_list) {}
 
   tex_string_iterator<T>& operator=(const tex_string_iterator & iter) {
       if (this != &iter) {
           _begin = iter._begin;
           _end = iter._end;
           _it = iter._it;
           _bra = iter._bra;
           _ket = iter._ket;
           _comments_list = iter._comments_list;
       }
       return *this;
   }
 
   tex_string_iterator() {}
 
   ~tex_string_iterator() {}
 
   const T& operator*() const { return *_it; }
 
   const T* operator->() const { return _it; }
 
   tex_string_iterator<T>& operator++() {
 
       if (*_it != _bra) {
           if (++_it == _end)
               return *this;
           if (*_it != _bra)
               return *this;
       }
 
       for (const auto & comment : _comments_list) {
           if (comment.begin() == _it) {
               _it = comment.end();
               if (_it == _end)
                   break;
 
               if (*(++_it) != _bra)
                   break;
           }
       }
 
       return *this;
   }
 
   tex_string_iterator<T> operator++(int) {
       tex_string_iterator<T> tmp = *this;
       ++*this;
       return tmp;
   }
 
   tex_string_iterator<T>& operator--() {
 
       if (*_it != _ket) {
           --_it;
           if (*_it != _ket)
               return *this;
       }
 
       for (const auto & comment :_comments_list) {
           if (comment.end() == _it) {
               _it = comment.begin();
               if (_it == _begin)
                   break;
 
               if (*(--_it) != _ket)
                   break;
           }
       }
 
       return *this;
   }
 
   tex_string_iterator<T> operator--(int) {
       tex_string_iterator<T> tmp = *this;
       --*this;
       return tmp;
   }
 
   template <class R>
   friend bool operator==(const tex_string_iterator<R> &, const tex_string_iterator<R> &);
 
   template <class R>
   friend bool operator!=(const tex_string_iterator<R> &, const tex_string_iterator<R> &);
 
private:
   string_iterator _begin;
   string_iterator _end;
   string_iterator _it;    //current iterator
   T _bra;                 //open comment symbol
   T _ket;                 //closed comment symbol
   std::list<boost::iterator_range<string_iterator>> _comments_list;
 
   void make_comments_list() {
       if (_begin == _end)
           return;
 
       auto first = _begin;
       auto second = _begin;
 
       while ((first = std::find(first, _end, _bra)) != _end) {
           second =  std::find(first, _end, _ket);
           _comments_list.push_back(boost::iterator_range<string_iterator>(first, second));
           first = second;
       }
   }
};
 
template<class T>
inline bool operator==(const tex_string_iterator<T> & x, const tex_string_iterator<T> & y) {
   return (x._it == y._it);
}
 
template<class T>
inline bool operator!=(const tex_string_iterator<T> & x, const tex_string_iterator<T> & y) {
   return !(x == y);
}
 
int main()
{
 
   std::string buffer = "text text %comment comment\n text nocomment";
 
   tex_string_iterator<char> ibegin(buffer.begin(), buffer.end(), '%', '\n');
   tex_string_iterator<char> iend(buffer.end(), buffer.end());
 
   boost::regex_iterator<tex_string_iterator<char>> it(ibegin, iend, boost::regex("(com)"));
   boost::regex_iterator<tex_string_iterator<char>> end;
   for (; it != end; ++it) {
       std::cout << it->str(1) << std::endl;
   }
 
  return 0;
}
 


Сейчас меня такой вариант устраивает..
Но хотелось бы и другие соображения/подходы пообсуждать. Улыбающийся
  
« Последнее редактирование: Апрель 20, 2014, 14:26 от m_ax » Записан

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

Arch Linux Plasma 5
sergek
Гипер активный житель
*****
Offline Offline

Сообщений: 872


Мы должны приносить пользу людям.


Просмотр профиля
« Ответ #1 : Апрель 20, 2014, 18:02 »

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

Qt 5.13.0 Qt Creator 5.0.1
Win10, Ubuntu 20.04
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #2 : Апрель 20, 2014, 19:51 »

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

Ну вобщем то сейчас парсер и делает свою работу.. В том смысле, что сами регулярки не изменились: как если бы комментов и вовсе не было.. (т.е. за комменты отвечает не сам парсер, а итератор: фактически обёртка над string::const_iterator)

Почему не хочу делать влоб - хочется избежать затратного переаллоцирования памяти при удалении комментов.. (особенно если входной файл большой)

А вообще, в таких ситуациях обычно как поступают? (ситуация то вполне рядовая..?) Просто интересно.. 
Записан

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

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

Сообщений: 2095



Просмотр профиля
« Ответ #3 : Апрель 20, 2014, 20:10 »

Хотя в моём случае объёмы не такие уж и страшные, максимум порядка мегабайт.. Может и вправду влоб?
Записан

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

Arch Linux Plasma 5
sergek
Гипер активный житель
*****
Offline Offline

Сообщений: 872


Мы должны приносить пользу людям.


Просмотр профиля
« Ответ #4 : Апрель 20, 2014, 20:18 »

А вообще, в таких ситуациях обычно как поступают? (ситуация то вполне рядовая..?) Просто интересно.. 
По-моему, так и поступают, сначала подготавливают текст, потом подают на вход специализированного процессора. Затраты на распределение памяти сейчас минимальные. Для себя я давно решил- лучше сделать проще и понятнее, чем эффективнее и сложнее. Ну, разве что, если задача критична к затратам, или хочется сделать красиво из любви к искусству...
Записан

Qt 5.13.0 Qt Creator 5.0.1
Win10, Ubuntu 20.04
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #5 : Апрель 20, 2014, 20:33 »

Может и вправду влоб?
А завтра вам понадобится доставать информацию из комментариев... Улыбающийся Например, соотношение информации и комментариев в файле.
Сейчас, как я понял, вы руками разбираете файл по токенам, а почему не описать правила для такого разбора? И случай с комментарием будет одним из правил. Сейчас, вы сможете просто игнорировать текст комментария, а завтра обрабатывать его по вновь описанным правилам.
Понимаете куда клоню? Не пробовали или медленно? Улыбающийся
Записан
sergek
Гипер активный житель
*****
Offline Offline

Сообщений: 872


Мы должны приносить пользу людям.


Просмотр профиля
« Ответ #6 : Апрель 20, 2014, 20:42 »

А завтра вам понадобится доставать информацию из комментариев... Улыбающийся .
Значит, из текста выделить комментарии и подать на вход другого процессора. Который может только комментарии обрабатывать. Вы поняли, куда я клонил? Подмигивающий
Записан

Qt 5.13.0 Qt Creator 5.0.1
Win10, Ubuntu 20.04
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #7 : Апрель 20, 2014, 20:52 »

Может и вправду влоб?
А завтра вам понадобится доставать информацию из комментариев... Улыбающийся Например, соотношение информации и комментариев в файле.
Сейчас, как я понял, вы руками разбираете файл по токенам, а почему не описать правила для такого разбора? И случай с комментарием будет одним из правил. Сейчас, вы сможете просто игнорировать текст комментария, а завтра обрабатывать его по вновь описанным правилам.
Понимаете куда клоню? Не пробовали или медленно? Улыбающийся

Вот я и боюсь, а что если завтра, действиетельно, понадобиться?)
Если так, то используя подход с пользовательским итератором, я могу, например, просто написать другой специализированный итератор для других целей..

Сейчас, к сожалению, моя задача не сводится к банальному разбиванию текста на токены.. Поэтому я использую несколько проходжов: вначале регуляркой, затем токенайзером (я об этом писал в теме igor'са "разбор QString")..

Есть ещё мысли покурить в сторону boost::spirit.. Но пока только мысли)
А Вы как бы поступили? (Да, парсю такой формат: http://en.wikipedia.org/wiki/BibTeX + ещё один очень схожий)  
« Последнее редактирование: Апрель 20, 2014, 20:55 от m_ax » Записан

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

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

Сообщений: 4350



Просмотр профиля
« Ответ #8 : Апрель 20, 2014, 20:55 »

Есть ещё мысли покурить в сторону boost::spirit.. Но пока только мысли)
Таки про него я и намекаю. Смотрите сразу новую версию, думаю вам понравиться. Улыбающийся
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #9 : Апрель 20, 2014, 21:23 »

Есть ещё мысли покурить в сторону boost::spirit.. Но пока только мысли)
Таки про него я и намекаю. Смотрите сразу новую версию, думаю вам понравиться. Улыбающийся

Спасибо, посмотрю.. Надеюсь, проблема с комментариями там элегантнее решается)
Записан

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

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

Сообщений: 4350



Просмотр профиля
« Ответ #10 : Апрель 20, 2014, 21:35 »

Спасибо, посмотрю.. Надеюсь, проблема с комментариями там элегантнее решается)
Одной строкой описания правила. Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Апрель 21, 2014, 11:36 »

Комментарий начинается с определённого символа и заканчивается другим.
К сожалению - необязательно, примеры
Цитировать
void foo();   // here can be anything /* ' " etc
...
f 1 2 3  # 44 vertices

Метод "влоб" - это просто удалить из строки все комменты, и уже дальше парсить её.. Но этот вариант не очень(
Для этого, QString::split и др "быстрых" решений есть хороший тест: пользователь хочет видеть где ошибка, напр подсветкой
Цитировать
f 1 2.0 3
Error in line 33: integer expected
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #12 : Апрель 21, 2014, 11:48 »

К сожалению - необязательно, примеры
Обязательно.
В этих примерах комментарий начинается от // и #, а заканчивается eol (концом строки).
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #13 : Апрель 21, 2014, 12:00 »

К сожалению - необязательно, примеры
Обязательно.
В этих примерах комментарий начинается от // и #, а заканчивается eol (концом строки).


Это не принципиально.. Вот Вам вариант с более слжными комментами:

Код
C++ (Qt)
#include <string>
#include <list>
#include <boost/regex.hpp>
#include <boost/range/istream_range.hpp>
#include <algorithm>
 
template <class T = char>
class tex_string_iterator : public std::iterator<std::bidirectional_iterator_tag, T, ptrdiff_t, const T*, const T&>
{
public:
   typedef T char_type;
   typedef std::basic_string<T> string_type;
   typedef typename std::basic_string<T>::const_iterator string_iterator;
   typedef boost::regex regex_type;
 
   tex_string_iterator(string_iterator begin, string_iterator end, const regex_type & comment)
       : _begin(begin), _end(end), _it(begin), _comment(comment)
   {
       make_comments_list();
   }
 
   tex_string_iterator(const tex_string_iterator<T>& iter)
       : _begin(iter._begin), _end(iter._end), _it(iter._it),
         _comment(iter._comment), _comments_list(iter._comments_list) {}
 
   tex_string_iterator<T>& operator=(const tex_string_iterator & iter) {
       if (this != &iter) {
           _begin = iter._begin;
           _end = iter._end;
           _it = iter._it;
           _comment = iter._comment;
           _comments_list = iter._comments_list;
       }
       return *this;
   }
 
   tex_string_iterator() {}
 
   ~tex_string_iterator() {}
 
   const T& operator*() const { return *_it; }
 
   const T* operator->() const { return _it; }
 
   tex_string_iterator<T>& operator++() {
       auto cur = _it;
       ++_it;
       for (const auto & comment : _comments_list) {
           if ((comment.begin() == _it) || (comment.begin() == cur)) {
               _it = comment.end();
               if (_it == _end)
                   break;
               ++_it;
           }
       }
 
       return *this;
   }
 
   tex_string_iterator<T> operator++(int) {
       tex_string_iterator<T> tmp = *this;
       ++*this;
       return tmp;
   }
 
   tex_string_iterator<T>& operator--() {
       auto cur = _it;
       --_it;
       for (const auto & comment : _comments_list) {
           if ((comment.end() == _it) || (comment.end() == cur)) {
               _it = comment.begin();
               if (_it == _begin)
                   break;
               --_it;
           }
       }
 
       return *this;
   }
 
   tex_string_iterator<T> operator--(int) {
       tex_string_iterator<T> tmp = *this;
       --*this;
       return tmp;
   }
 
   template <class R>
   friend bool operator==(const tex_string_iterator<R> &, const tex_string_iterator<R> &);
 
   template <class R>
   friend bool operator!=(const tex_string_iterator<R> &, const tex_string_iterator<R> &);
 
private:
   string_iterator _begin;
   string_iterator _end;
   string_iterator _it;    //current iterator
   regex_type _comment;
   std::list<boost::iterator_range<string_iterator>> _comments_list;
 
   void make_comments_list() {
       if (_begin == _end)
           return;
 
       auto first = _begin;
       boost::match_results<string_iterator> what;
       while (boost::regex_search(first, _end, what, _comment)) {
           _comments_list.push_back(boost::iterator_range<string_iterator>(what[1].first, what[1].second));
           first = what[1].second;
       }
   }
};
 
template<class T>
inline bool operator==(const tex_string_iterator<T> & x, const tex_string_iterator<T> & y) {
   return (x._it == y._it);
}
 
template<class T>
inline bool operator!=(const tex_string_iterator<T> & x, const tex_string_iterator<T> & y) {
   return !(x == y);
}
 
 
int main()
{
 
   boost::regex expr("([#\\\\][^\\n]*)");
 
   std::string buffer = "text#comment\ntext\\comment ";
 
   tex_string_iterator<char> iter(buffer.begin(), buffer.end(), expr);
   tex_string_iterator<char> end(buffer.end(), buffer.end(), expr);
 
   for (; iter != end; ++iter) {
       std::cout << *iter;
   }
 
   return 0;
}
 

Цитировать
Для этого, QString::split и др "быстрых" решений есть хороший тест: пользователь хочет видеть где ошибка, напр подсветкой
Это тоже не проблема.. Я же буду знать итератор, на том месте где проблема возникнет..  
« Последнее редактирование: Апрель 21, 2014, 12:52 от m_ax » Записан

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

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

Сообщений: 4350



Просмотр профиля
« Ответ #14 : Апрель 21, 2014, 12:09 »

Что значит не принципиально? Улыбающийся
Комментарии всегда начинаются с определённой последовательности и заканчиваются определённой последовательностью. Обязательно. Улыбающийся
Записан
Страниц: [1] 2 3 4   Вверх
  Печать  
 
Перейти в:  


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