Russian Qt Forum

Qt => Вопросы новичков => Тема начата: Igors от Май 17, 2015, 07:34



Название: Регулярки
Отправлено: Igors от Май 17, 2015, 07:34
Добрый день

Наконец-то (первый раз в жизни) мне понадобились регулярные выражения! :) Нужно очень немного - "джокеры"

* - любое кол-во любых символов
? - один любое символ
- уметь искать "полные слова" (с джокерами и без)

Ладно, открываю букварь, вроде мне подходит QRegExp::Wildcard (PatternSyntax). Но тут растерялся
Цитировать
// To test a string against a wildcard expression, use exactMatch(). For example:

QRegExp rx("*.txt");
rx.setPatternSyntax(QRegExp::Wildcard);
rx.exactMatch("README.txt");        // returns true
rx.exactMatch("welcome.txt.bak");   // returns false
Вопросы

1) Так что, с Wildcard можно юзать только exactMatch, остальное не будет работать или как?
2) Во втором вызове (где returns false) мне надо чтобы находило (напр поиск "contains"). Как это сделать?
3) Чтобы искало полные слова - надо "обрамлять" \\b, правильно?

Спасибо


Название: Re: Регулярки
Отправлено: kambala от Май 17, 2015, 12:16
1) похоже на то
2) ну придется звездочками хитрить: "*.txt*"
3) \b в Wildcard не прокатит. регулярка "\\b.+?\\.txt.*?\\b" (для QRegExp вместо знаков вопроса надо использовать setMinimal(true), QRegularExpression таким не страдает)


Название: Re: Регулярки
Отправлено: Igors от Май 17, 2015, 13:31
1) похоже на то
2) ну придется звездочками хитрить: "*.txt*"
3) \b в Wildcard не прокатит. регулярка "\\b.+?\\.txt.*?\\b" (для QRegExp вместо знаков вопроса надо использовать setMinimal(true), QRegularExpression таким не страдает)
Поэкспериментировал, работает indexIn, QString::indexOf(QRegExp). Слово не выделяет (\\b не понимает как и любой слеш), ну не беда, сам написал.  

Тут такие проблемы

1) Строку паттерна вводит юзер, поэтому выбран WildCard. Как-то строку "транслировать" (чтобы задействовать "полный" режим) - думал, но не видно как

2) Было бы хорошо использовать "номер" (напр символ #). Пример: есть 100 объектов с именами "Sphere 1", "Sphere 2",... "Sphere 100". Или вообще имя одно "Sphere" у всех. Полезной опцией была бы возможность убрать/добавить "номера" (1..100). Как это половчее сделать?

Спасибо



Название: Re: Регулярки
Отправлено: kambala от Май 17, 2015, 14:16
1)
  • * -> .* (это 0 и более символов, для 1 и более — .+)
  • ? -> .
2) "Sphere ?\\d{0,3}?" — совпадет со "Sphere", "Sphere ", "Sphere123", "Sphere 999". Если жестко максимум 100, то можно что-то типа "Sphere ?(?:\\d{0,2}|100)?", но лучше погуглить :) — уверен, таких вопросов поднималась куча


Название: Re: Регулярки
Отправлено: Bepec от Май 17, 2015, 15:00
Я использую deelx.h библиотечку, один ашник, поддерживает полностью все regExp, чисто сишный интерфейс.
Конечно, для себя имеется небольшая обвязочка на Qt, но вам наверно пойдёт и так :)

PS Qt-шный regexp до пятерки отвратный :)


Название: Re: Регулярки
Отправлено: Igors от Май 17, 2015, 15:29
1)
  • * -> .* (это 0 и более символов, для 1 и более — .+)
  • ? -> .
Не зрозумiв. На всяк випадок ще раз перевiрив ? - робить добре

2) "Sphere ?\\d{0,3}?" — совпадет со "Sphere", "Sphere ", "Sphere123", "Sphere 999". Если жестко максимум 100, то можно что-то типа "Sphere ?(?:\\d{0,2}|100)?", но лучше погуглить :) — уверен, таких вопросов поднималась куча
Жестко не будет, есть варианты - напр нумерация в рамках объекта-парента или глобальная. Если просто "добавить в хвост" (убрать из хвоста) то можно без всякого RegExp. Но все так от него пищат что я аж засомневался - ну а вдруг есть "более элегантные решения" (а я тут прусь с велосипедом :)). Напр возможен такой случай
Код:
Sphere 1 Red 
Sphere 2 Blue
...
Не то чтобы это "так уж важно" (руками переименует, не облезет), но все-таки... Неплохо бы напр так
Цитировать
Sphere # *
Это паттерн что ввел юзер. Если бы можно было выцепить "решетку" (число) чтобы только его заменить/удалить


Название: Re: Регулярки
Отправлено: kambala от Май 17, 2015, 23:34
1) . (точка) означает любой символ в регулярном выражении, т.е. то же самое, что и ? в вайлдкарде

"Sphere # *" -> "Sphere (\\d+).*", число будет в rx.cap(1)


Название: Re: Регулярки
Отправлено: Igors от Май 18, 2015, 10:46
Непонятки с replace. Неужели замещающая строка может быть только фиксированной? Напр такой паттерн
Цитировать
? * *
Вот я хочу заменить только первую "звездочку" не трогая остальное. Как мне это сделать? Если использовать просто " * " то это может соответствовать совсем другому контексту (во всей строке)

Спасибо


Название: Re: Регулярки
Отправлено: kambala от Май 18, 2015, 10:55
чтобы регулярка совпала со звездочкой, надо эту звездочку экранировать бэкслэшем. хотя не совсем понятно зачем для замены первого вхождения звездочки в строке использовать регулярку…


Название: Re: Регулярки
Отправлено: Igors от Май 18, 2015, 11:29
чтобы регулярка совпала со звездочкой, надо эту звездочку экранировать бэкслэшем. хотя не совсем понятно зачем для замены первого вхождения звездочки в строке использовать регулярку…
Не выходит, ставлю \\ вообще не находит. Фраза из букваря
Цитировать
In the mode Wildcard, the wildcard characters cannot be escaped. In the mode WildcardUnix, the character '\' escapes the wildcard.
Разницы между Wildcard/WildcardUnix пока не увидел. Что здесь означает "escaped"?

Спасибо


Название: Re: Регулярки
Отправлено: kambala от Май 18, 2015, 14:02
escaped = бэкслэш перед спец. символом. значит матчнуть символ * или ? в вайлдкарде можно только в режиме WildcardUnix, дописав перед символом бэкслэш


Название: Re: Регулярки
Отправлено: Igors от Май 18, 2015, 14:54
escaped = бэкслэш перед спец. символом. значит матчнуть символ * или ? в вайлдкарде можно только в режиме WildcardUnix, дописав перед символом бэкслэш
А каков должен быть эффект этого "матчения"?  :) Напр
Цитировать
Sphere *
Sphere \\*
В чем разница?


Название: Re: Регулярки
Отправлено: kambala от Май 18, 2015, 16:13
в режиме WildcardUnix первое матчнет "Sphere ляляля", а второе — "Sphere *"


Название: Re: Регулярки
Отправлено: Igors от Май 18, 2015, 17:07
в режиме WildcardUnix первое матчнет "Sphere ляляля", а второе — "Sphere *"
Понял, спасибо. К сожалению, пользы от этого немного  :)


Название: Re: Регулярки
Отправлено: Igors от Май 19, 2015, 11:58
Хотелось бы иметь возможность "удалять слово". Пример
Цитировать
arm_lo 1
arm_hi 2
Найти легко, паттерн  "arm_* *". Но как мне удалить номера (1, 2)?


Название: Re: Регулярки
Отправлено: Bepec от Май 19, 2015, 16:05
хм. удалить можно, используя предпросмотр назад и вперед.

Т.е. (?<=arm_..)* найдёт лишь 1 и 2. Ну и зная их позиции спокойно удаляем.

ссылка на вики, раздел "Просмотр вперёд и назад"

https://ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D0%B5_%D0%B2%D1%8B%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F#.D0.93.D1.80.D1.83.D0.BF.D0.BF.D0.B8.D1.80.D0.BE.D0.B2.D0.BA.D0.B0_.D0.B1.D0.B5.D0.B7_.D0.BE.D0.B1.D1.80.D0.B0.D1.82.D0.BD.D0.BE.D0.B9_.D1.81.D0.B2.D1.8F.D0.B7.D0.B8

PS в Qt4 QRegExp не поддерживал эту возможность.


Название: Re: Регулярки
Отправлено: kambala от Май 19, 2015, 17:51
в «регулярном» режиме это делается легко через захват числа в () и QRegExp::pos()


Название: Re: Регулярки
Отправлено: Igors от Май 20, 2015, 08:34
в «регулярном» режиме это делается легко через захват числа в () и QRegExp::pos()
Но не в wildcard режиме. Ну ничего, перебьются

Т.е. (?<=arm_..)* найдёт лишь 1 и 2. Ну и зная их позиции спокойно удаляем.
Ну расчет на юзера который может и про джокер-звездочку никогда не слыхал - он вводит пвттерн


Название: Re: Регулярки
Отправлено: Bepec от Май 20, 2015, 10:30
Ну так напишите собственный псевдоязык.
А дальше уже преобразуйте в regExp.

PS какой то язык пользователь всё равно должен будет запомнить, иначе не сможет работать с программой.


Название: Re: Регулярки
Отправлено: Kurles от Май 20, 2015, 16:03
Хотелось бы иметь возможность "удалять слово". Пример
Цитировать
arm_lo 1
arm_hi 2
Найти легко, паттерн  "arm_* *". Но как мне удалить номера (1, 2)?

Ну если именно с помощью регекспов, то первое, что в голову пришло:
Код
C++ (Qt)
   QRegExp re("(arm_\\w+)( \\d+)");
   QString test("arm_lo 12");
   int i = re.indexIn(test);
   if (i >= 0) {
       QString corrected = test;
       corrected.remove(i + re.cap(1).length(), re.cap(2).length());
       qDebug() << corrected;
   }
 
Но тут уже не Wildcard matching.


Название: Re: Регулярки
Отправлено: Igors от Май 20, 2015, 16:58
Ну так напишите собственный псевдоязык.
А дальше уже преобразуйте в regExp.
Были мысли в этом направлении ("решетка"), но отказался - при малейшем наращивании ф-ционала светит "тот самый велосипед"

PS какой то язык пользователь всё равно должен будет запомнить, иначе не сможет работать с программой.
Ну я пользовался "звездочкой" в Norton Commander не подозревая о существовании регулярок.

Ну если именно с помощью регекспов, то первое, что в голову пришло:
Код
C++ (Qt)
   QRegExp re("(arm_\\w+)( \\d+)");
   QString test("arm_lo 12");
   int i = re.indexIn(test);
   if (i >= 0) {
       QString corrected = test;
       corrected.remove(i + re.cap(1).length(), re.cap(2).length());
       qDebug() << corrected;
   }
 
Но тут уже не Wildcard matching.
А как юзверь в UI введет "двойку" которая нужна для cap(2) ?

Хорошо, пусть более скромная задача
Цитировать
ren *.cpp *.cp1
Это работает напр в Far. Как добиться того же? Т.е. юзер вводит
Цитировать
Search for: *.cpp
Replace with: *.cp1


Название: Re: Регулярки
Отправлено: kambala от Май 20, 2015, 17:06
как преобразовать вайлдкард в полноценную регулярку я уже писал, разберем на примере:
1) вайлдкард *.cpp превращаем в регулярку (.+)\.cpp (можно и .* вместо .+, но вряд ли нас интересуют файлы с пустым именем: ".cpp")
2) имя файла в rx.cap(1)
3) результат: rx.cap(1) + ".cp1"


Название: Re: Регулярки
Отправлено: Bepec от Май 20, 2015, 17:17
Вы опять хотите чтобы пользователь без знания wildcard regexp и простейшего псевдоязыка мог пользоваться вашей программой? Мечтатель :)


Название: Re: Регулярки
Отправлено: Igors от Май 20, 2015, 17:58
как преобразовать вайлдкард в полноценную регулярку я уже писал, разберем на примере:
1) вайлдкард *.cpp превращаем в регулярку (.+)\.cpp (можно и .* вместо .+, но вряд ли нас интересуют файлы с пустым именем: ".cpp")
2) имя файла в rx.cap(1)
3) результат: rx.cap(1) + ".cp1"
Та же проблема что и в ответе Kurles, только 1 вместо 2. Но ведь может быть и так
Цитировать
ren test.c* test1.c*
или так
Цитировать
ren *.c* *.cpp


Название: Re: Регулярки
Отправлено: kambala от Май 20, 2015, 19:16
где проблема? каждую звездочку мы заменяем на (.+), индексы для cap() начиная с 1 — это номер скобок если считать слева направо.

"asd(.+)qwe(.+)zxc" совпадет например с "000asd123qwe321zxcлрнпии", где rx.cap(0) = asd123qwe321zxc (полное совпадение), rx.cap(1) = 123, rx.cap(2) = 321

для ren *.c* *.cpp браться будет только rx.cap(1)


Название: Re: Регулярки
Отправлено: Igors от Май 21, 2015, 06:09
где проблема? каждую звездочку мы заменяем на (.+), индексы для cap() начиная с 1 — это номер скобок если считать слева направо.

"asd(.+)qwe(.+)zxc" совпадет например с "000asd123qwe321zxcлрнпии", где rx.cap(0) = asd123qwe321zxc (полное совпадение), rx.cap(1) = 123, rx.cap(2) = 321

для ren *.c* *.cpp браться будет только rx.cap(1)
Да, мы можем выцепить все куски которые найдены на месте джокеров - ну а толку? Это же совсем не значит что все их надо менять, это определяется выходным паттерном задаваемым пользователем (replace with). Тот же пример
Цитировать
ren test.c* test1.c*
Требуется заменить test на test1. Какому cap(?) он соответствует?
Еще пример
Цитировать
ren test*.c*  tst0.c1
А здесь устраивает и та замена что в QString - но как это вычислить?

Edit: более четко можно сформулировать так. Пусть есть строка поиска
Цитировать
Search for: test*.c*
Но по ней мы ничего еще заменять не можем. В зависимости от строки замены
Цитировать
Replace with: test1.c*
Replace with: test*.cpp
Replace with: test.c
Это будут 3 разные замены. И все они работают в командной строке, Far и др. Может это вообще не регулярки  ???

По классике
Цитировать
Вот так всю жизнь и проживешь без собаки ...
:)


Название: Re: Регулярки
Отправлено: kambala от Май 21, 2015, 10:23
Цитировать
Может это вообще не регулярки
естественно нет, это вайлдкарды

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


Название: Re: Регулярки
Отправлено: Igors от Май 23, 2015, 08:15
Цитировать
Может это вообще не регулярки
естественно нет, это вайлдкарды
Ну вот, как только я хочу воспользоваться готовым, проверенным решением - ни фига  :'(
Пришлось сделать самому (аттач). Попинайте

Edit: исправил бажок, обновил (2)