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

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

Страниц: [1] 2 3 ... 8   Вниз
  Печать  
Автор Тема: Как заменить неизвестное заранее число вхождений в QRegExp ?  (Прочитано 56634 раз)
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« : Июнь 05, 2016, 17:40 »

Есть многострочный HTML-текст (по сути, валидный XML), в котором могут встречаться такие конструкции:

Код:
<span разные_атрибуты>  </span>
<span разные_атрибуты>    </span>
<span разные_атрибуты> </span>


Да, это именно "пробельный" текст с разным количеством пробелов.

Задача: заменить все такие вхождения на такие:

Код:
<span разные_атрибуты>&#65533;&#65533;</span>
<span разные_атрибуты>&#65533;&#65533;&#65533;&#65533;</span>
<span разные_атрибуты>&#65533;</span>


В других местах HTML-кода заменять пробелы на "&#65533;" не требуется.

Обрабатывать нужно именно регулярными выражениями. Потому что, например, парсинг в DOM (с последующим обратным преобразованием) не сделает текстовую ноду из одних пробелов внутри ноды span.

Вопрос: как это сделать регулярками? Вот заготовка:

Код:
QRegExp replaceSpaceTagsEx("<span.*>(\\s*)</span>");
replaceSpaceTagsEx.setMinimal(true);
htmlCode.replace(replaceSpaceTagsEx, "?????");


Что надо прописать вместо знаков вопроса? Или надо делать как-то по-другому?
« Последнее редактирование: Июнь 05, 2016, 19:31 от xintrea » Записан

Собираю информацию по крупицам
http://webhamster.ru
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #1 : Июнь 05, 2016, 18:17 »

Boost::xpressive может такое делать) Можно использовать его, а можно и свой велосипед написать, подсмотрев как это сделано там (в xpressive).
Фактически нужно написать свою функцию, аналог http://www.boost.org/doc/libs/1_61_0/doc/html/xpressive/user_s_guide.html#boost_xpressive.user_s_guide.string_substitutions:
Код
C++ (Qt)
QString regex_replace(const QString & input, const RegExp & regex, const std::function<QString(const QString &)> & format_func);
 
Записан

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

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

Сообщений: 754



Просмотр профиля WWW
« Ответ #2 : Июнь 05, 2016, 19:30 »

Boost::xpressive может такое делать) Можно использовать его, а можно и свой велосипед написать, подсмотрев как это сделано там (в xpressive).
Фактически нужно написать свою функцию, аналог http://www.boost.org/doc/libs/1_61_0/doc/html/xpressive/user_s_guide.html#boost_xpressive.user_s_guide.string_substitutions:
Код
C++ (Qt)
QString regex_replace(const QString & input, const RegExp & regex, const std::function<QString(const QString &)> & format_func);
 

Как-то это слишком сложно для такой простой задачи.

А Boost я не смогу воспользоваться потому что концепт разрабатываемой программы - Qt-only.

Есть что-нибудь подубовее и попроще?
Записан

Собираю информацию по крупицам
http://webhamster.ru
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #3 : Июнь 05, 2016, 23:33 »

Цитировать
Как-то это слишком сложно для такой простой задачи.
Вот для более гибкого буста это действительно, простая задача) А для Qt простого решения, похоже нет (могу ошибаться)
Я вот что примерно предлагал
Код
C++ (Qt)
template <class FormatFunc>
QString regex_replace(const QString & input, const QRegExp & regex, FormatFunc format_func)
{
   int pos = 0;
   QStringList list;
   QList<QPair<int, int>> ranges;
 
   while ((pos = regex.indexIn(input, pos)) != -1)
   {
       ranges << qMakePair(regex.pos(1), regex.pos(1) + regex.cap(1).size());
       list << format_func(regex.cap(1));
       pos += regex.matchedLength();
   }
 
   int space_count = 0;
   int rep_space_count = 0;
   auto it = list.begin();
   for (auto & r : ranges)
   {
       space_count += r.second - r.first;
       rep_space_count += it->size();
   }
 
   QString out;
   out.reserve(input.size() - space_count + rep_space_count);
 
   it = list.begin();
   pos = 0;
 
   for (auto & r : ranges)
   {
       out.append(QStringRef(&input, pos, r.first-pos));
       out.append(*it++);
       pos = r.second;
   }
   out.append(QStringRef(&input, pos, input.size()-pos));
 
   return out;
}
 
int main()
{
   QString str = "<span>  </span> text text text <span> </span> text TeXt <span>         </span>bla-bla-bla";
 
   QRegExp rx("<span.*>(\\s*)</span>");
   rx.setMinimal(true);
 
   qDebug() << regex_replace(str, rx, [](const QString & str)->QString
   {
       QString rep = "&#38;#65533;";
 
       return  rep.repeated(str.size());
   });
 
   return 0;
}
 
« Последнее редактирование: Июнь 05, 2016, 23:38 от m_ax » Записан

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

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

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Июнь 06, 2016, 06:45 »

Эх, напишу-ка я файнд-реплейс Улыбающийся
Код
C++ (Qt)
#include <QtWidgets>
 
typedef QPair<int, int> TPair;
typedef QVector<TPair> TVec;
 
TVec CollectPairs( const QString & src, const QString & begStr, const QString & endStr )
{
TVec dst;
int pos = 0;
while (true) {
int beg = src.indexOf(begStr, pos);
if (beg < 0) return dst;
int pos2 = pos = beg + begStr.size();
 
int end = src.indexOf(endStr, pos);
if (end < 0) return dst;
pos = end + endStr.size();
 
for (int mid = end - 1; mid >= pos2; --mid) {
if (src[mid] == ' ') continue;
if (src[mid] == '>')
dst.push_back(TPair(mid + 1, end));
break;
}
}
return dst;
}
 
QString ReplaceBlanks( const QString & src )
{
QString dst;
const QString & spaceTxt("&#38;#65533;");
int srcPos = 0;
TVec vec = CollectPairs(src, "<span", "</span>");
for (int i = 0; i < vec.size(); ++i) {
int beg = vec[i].first;
int end = vec[i].second;
dst += QStringRef(&src, srcPos, beg - srcPos);
for (int j = beg; j < end; ++j)
dst += spaceTxt;
srcPos = end;
}
 
dst += QStringRef(&src, srcPos, src.size() - srcPos);
return dst;
}
 
int main(int argc, char *argv[])
{
const char * testStr =
"<span разные_атрибуты>  </span>\n"
"<span разные_атрибуты>    </span>\n"
"<span разные_атрибуты> </span>";
 
qDebug() << ReplaceBlanks(testStr);
return 0;
}
 
Записан
zavitaliy
Гость
« Ответ #5 : Июнь 06, 2016, 09:52 »

Простой вариант без функций

Код:
    QString test = "<span разные_атрибуты>  </span>"
                   "<span разные_атрибуты>    </span>"
                   "<span разные_атрибуты> </span>";

    QRegExp rx = QRegExp("(<span.*>)(\\s*)(</span>)");
    rx.setMinimal(true);
    int pos = 0;
    while ( (pos=rx.indexIn(test,pos)) != -1) {
        QString tmp = QString("&#65533;").repeated( rx.cap( 2 ).length() );
        test.replace( pos,
                      rx.matchedLength(),
                      rx.cap(1) + tmp + rx.cap(3)
                      );
        pos += rx.matchedLength() + tmp.length() - rx.cap( 2 ).length();
    }

    qDebug().noquote() << test;
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #6 : Июнь 06, 2016, 10:48 »

Цитировать
Эх, напишу-ка я файнд-реплейс  Улыбающийся
Аха, аха) А потом ещё с десяток штук: вдруг ТСу завтра что-нить другое на что-нить третье придётся заменять?)

Цитировать
Простой вариант без функций

Так категорически нельзя делать..(
Записан

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

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

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Июнь 06, 2016, 16:51 »

Аха, аха) А потом ещё с десяток штук: вдруг ТСу завтра что-нить другое на что-нить третье придётся заменять?)
А у Вас, стало быть, уже все "обобщено"?  Улыбающийся
Записан
GreatSnake
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2921



Просмотр профиля
« Ответ #8 : Июнь 06, 2016, 17:14 »

Я вот что примерно предлагал
А зачем так усложнять, если можно так
Код
C++ (Qt)
template <class FormatFunc>
QString regex_replace(const QString & input, const QRegExp & regex, FormatFunc format_func)
{
   QString out = input;
   int pos = 0;
   while ((pos = regex.indexIn(out, pos)) != -1)
   {
       auto s = format_func(regex.cap(1));
       out.replace( pos, regex.matchedLength(), s );
pos += s.length();
   }
   return out;
}
Записан

Qt 5.11/4.8.7 (X11/Win)
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #9 : Июнь 06, 2016, 18:30 »

Цитировать
А у Вас, стало быть, уже все "обобщено"?   Улыбающийся
Не всё, но гибче) Завтра ТСу понадобиться вместо тегов span что то другое.. Мне достаточно переписать регулярку, Вам лезть в обе функции.. Завтра ТСу потребуется в зависимости от того, что имено нах. между тегов заменять содержимое по какому-либо критерию: мне для этого нужно будет подкорректировать лямбду, а Вам?)

Цитировать
А зачем так усложнять, если можно так
Из-за вот этого репласе при каждом чихе:
Код
C++ (Qt)
out.replace( pos, regex.matchedLength(), s );
 
И потом, в общем случае, при этом варианте значение pos будет разным у оригинала и у out.. А это чревато..
Записан

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

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

Сообщений: 2921



Просмотр профиля
« Ответ #10 : Июнь 07, 2016, 10:59 »

Из-за вот этого репласе при каждом чихе:
И что в этом криминального?
Цитировать
И потом, в общем случае, при этом варианте значение pos будет разным у оригинала и у out.. А это чревато..
Нет, не будет, т.к. при парсинге используется только out, что совершенно не чревато  Подмигивающий
Записан

Qt 5.11/4.8.7 (X11/Win)
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #11 : Июнь 07, 2016, 11:18 »

Из-за вот этого репласе при каждом чихе:
И что в этом криминального?
Жирная она просто, эта репласе.. Если файл большой будет и придётся часто делать replace - то этот фрагмент может быстро стать узким местом..
Записан

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

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

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Июнь 07, 2016, 11:46 »

И что в этом криминального?
Ну то что вся строка (потенциально большая) копируется всякий раз

Нет, не будет, т.к. при парсинге используется только out, что совершенно не чревато  Подмигивающий
Все-таки так Вы ставите pos на начало "завершающего" (</span>) что неточно (в общем случае он может быть равен "стартовому").

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

Не всё, но гибче) Завтра ТСу понадобиться вместо тегов span что то другое.. Мне достаточно переписать регулярку, Вам лезть в обе функции.. Завтра ТСу потребуется в зависимости от того, что имено нах. между тегов заменять содержимое по какому-либо критерию: мне для этого нужно будет подкорректировать лямбду, а Вам?)
Может быть, но совершенно необязательно. А возможно за следующие полгода ни одной новой замены не возникнет вообще. Ну хорошо, допустим через год "опять", что бы я делал? Постарался  бы вспомнить свой старый текст. Если он прост - сделать это намного легче. Потом подумал что можно сделать лучше. Напр собирать все "пары" в контейнер - наверное ни к чему, лучше делать одним while. Возможно стоит сделать нормальный класс, напр
Код
C++ (Qt)
struct CReplacer {
CReplacer( const QString & src );
virtual bool Find( QStringRef & ref ) const;
virtual QString Replace( const QStringRef & ref ) const;
..
};
Но может и нет, если это опять "мелкий эпизод", то выделил ф-цию, закинул ее в утили, а остальное скопировал. В общем, как говорит народная мудрость
Цитировать
Тише едешь - дальше будешь
Улыбающийся
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #13 : Июнь 07, 2016, 13:21 »

Адище какое Улыбающийся
Находим через регексп текст внутри спана. Только В НАЙДЕННОМ ДИАПАЗОНЕ заменяем пробелы. Повторяем поиск с позиции конца заменененного диапазона.
Профит.
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
GreatSnake
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2921



Просмотр профиля
« Ответ #14 : Июнь 07, 2016, 13:57 »

Ну то что вся строка (потенциально большая) копируется всякий раз
Согласен, можно и не копировать, только ремарка была не к этому.

Цитировать
Все-таки так Вы ставите pos на начало "завершающего" (</span>) что неточно (в общем случае он может быть равен "стартовому")
С чего это? Никакого "завершающего" (</span>) там уже не будет. Внимательно смотрим код.

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

Адище какое Улыбающийся
Находим через регексп текст внутри спана. Только В НАЙДЕННОМ ДИАПАЗОНЕ заменяем пробелы. Повторяем поиск с позиции конца заменененного диапазона.
Профит.
+1
Записан

Qt 5.11/4.8.7 (X11/Win)
Страниц: [1] 2 3 ... 8   Вверх
  Печать  
 
Перейти в:  


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