Regex для замены вызовов в стиле printf синтаксисом ostream со сдвигом влево

Средство ведения журнала для нашего проекта C++ будет реорганизовано, чтобы использовать повторяющиеся операторы сдвига влево (наподобие Qt qDebug()) вместо вариативных функций в стиле printf.

Предположим, объект журнала называется logger. Допустим, мы хотим показать ip и порт сервера, к которому мы подключились. В текущей реализации используется:

logger.logf("connected to %s:%d", ip, port);

После рефакторинга приведенный выше вызов станет таким:

logger() << "connected to" << ip << ":" << port;

Ручная замена всех этих вызовов была бы чрезвычайно утомительной и подверженной ошибкам, поэтому, естественно, я хочу использовать регулярное выражение. В качестве первого прохода я мог бы заменить вызов .logf(...), получив

logger() "connected to %s:%d", ip, port;

Однако переформатирование этой строки в синтаксис сдвига влево вызывает у меня проблемы. Мне удалось создать отдельные регулярные выражения для захвата заполнителей printf и аргументы, разделенные запятыми. Тем не менее, я не знаю, как правильно соотнести их.

Чтобы избежать повторения довольно громоздких регулярных выражений, я буду использовать заполнитель (printf) для ссылки на заполнитель printf регулярное выражение (возвращающее именованную группу token) и (args) для ссылки на аргументы, разделенные запятыми. регулярное выражение (возвращающее именованную группу arg). Ниже я приведу результаты различных попыток, примененных к соответствующей части вышеуказанной строки, то есть:

"connected to %s:%d", ip, port
  • /(printf)(args)/g не дает совпадений.

  • /(printf)*(args)/g создает два совпадения, содержащие ip и port в именованной группе arg (но ничего в token).

  • /(printf)(args)*/g достигает противоположного результата: он производит два совпадения, содержащие %s и %d в именованной группе token, но ничего в arg.

  • /(printf)*(args)*/g возвращает 3 совпадения: первые два содержат %s и %d в token, третье содержит port в arg. Однако регулярное выражение101 сообщает «20 совпадений — 207 шагов» и, похоже, соответствует каждому символу.

  • Я подумал, что, возможно, мне нужно указать, что первая группа захвата всегда находится в двойных кавычках. Однако ни /"(printf)"(args)/g, ни /"(printf)(args)/g не дают совпадений.

  • /(printf)"(args)/g производит одно (неверное) совпадение, содержащее %d в группе token и ip в arg, а подстановка занимает всю строку между этими двумя строками (поэтому ввод # для строки подстановки приводит к "connected to %s:#, port. Очевидно, это не желаемый результат, но это единственная версия, в которой я мог получить хотя бы обе именованные группы в одном матче.

Любая помощь приветствуется.

Отредактировано для исправления неправильного форматирования.


person Community    schedule 08.07.2016    source источник
comment
Я не верю, что простое регулярное выражение может справиться со всеми возможностями здесь. Если бы я столкнулся с такой задачей, я бы потратил некоторое время и выкинул Perl-скрипт, чтобы просеять код и соответствующим образом преобразовать его.   -  person Sam Varshavchik    schedule 08.07.2016
comment
Это просто невозможно сделать с помощью регулярного выражения, по крайней мере, как это определено в информатике.   -  person user253751    schedule 08.07.2016
comment
Учтите, что следующая конструкция допустима для стиля printf: logger.logf("connected to %.*s:%-4d", 16, ip, port);.   -  person dxiv    schedule 08.07.2016
comment
@engineer14 [отвечаю на только что удаленный комментарий, но точка зрения остается в силе] Это не просто extra formatting. Например, %.*s — это обычный способ printf строк, не заканчивающихся нулем (или, если быть педантичным, массивы символов). Игнорирование спецификатора precision изменяет не только форматирование, но и саму семантику в этих случаях.   -  person dxiv    schedule 08.07.2016
comment
Сделать это полностью с помощью регулярных выражений и сделать все правильно чрезвычайно сложно. Даже строки в кавычках без интерполяций сложны. logger.logf("a" "b" "\""); Вероятно, проще написать небольшой посимвольный транслятор (например, на С++), чем правильно выполнять регулярные выражения.   -  person Gene    schedule 08.07.2016
comment
@dxiv Я действительно пытался (читай: изо всех сил пытался) разумно реализовать заполнители * в регулярном выражении, пока не понял, что не могу вспомнить ни одного случая, когда они использовались. Быстрый просмотр кодовой базы подтвердил это: кажется, нигде не используются директивы форматирования *. Между прочим, это означает, что количество заполнителей printf должно точно совпадать с количеством аргументов, что должно сделать решение (каким бы оно ни было) немного проще.   -  person    schedule 08.07.2016
comment
@carouselambra есть отзывы о моем ответе?   -  person Thomas Ayoub    schedule 12.07.2016
comment
извините, я хотел опубликовать скрипт Python, который я в конечном итоге использовал, но вмешалась реальная жизнь, скоро сделаю это   -  person    schedule 04.08.2016


Ответы (1)


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


Вы можете попробовать эту многоэтапную замену от максимального числа аргументов, которые у вас есть в решении, к минимальному (здесь я сделаю от 3 до 0).

Давайте рассмотрим logger.logf("connected to %s:%d some %s random text", ip, port, test);

Вы можете сопоставить это с этим регулярным выражением: logger.logf\("(.*?)(%[a-z])(.*?)(%[a-z])(.*?)(%[a-z])(.*?)",(.*?)(?:, (.*?))?(?:, (.*?))?\);, которое даст вам следующие группы:

1.  [75-88] `connected to `
2.  [88-90] `%s`
3.  [90-91] `:`
4.  [91-93] `%d`
5.  [93-99] ` some `
6.  [99-101]    `%s`
7.  [101-113]   ` random text`
8.  [115-118]   ` ip`
9.  [120-124]   `port`
10. [126-130]   `test`

Замените на logger() << "\1" << \8 << "\3" << \9 << "\5" << \10 << "\7";, чтобы получить

logger() ‹‹ "подключен к " ‹‹ ip ‹‹ ":" ‹‹ порт ‹‹ " какой-то " ‹‹ тест ‹‹ "случайный текст";


Теперь шаг с 2 аргументами, строка примера logger.logf("connected to %s:%d some random text", ip, port);, соответствующее регулярное выражение logger.logf\("(.*?)(%[a-z])(.*?)(%[a-z])(.*?)",(.*?)(?:, (.*?))?\);

Соответствие следующее:

1.  [13-26] `connected to `
2.  [26-28] `%s`
3.  [28-29] `:`
4.  [29-31] `%d`
5.  [31-48] ` some random text`
6.  [50-53] ` ip`
7.  [55-59] `port`

И строка замены: logger() << "\1" << \6 << "\3" << \7 << "\5"; выводит:

logger() ‹‹ "подключен к " ‹‹ ip ‹‹ ":" ‹‹ порт ‹‹ "случайный текст";


Введите logger.logf("Some %s text", port);

Регулярное выражение logger.logf\("(.*?)(%[a-z])(.*?)",(.*?)\);

Замена logger() << "\1" << \4 << "\3";

logger() ‹‹ "Некоторый " ‹‹ порт ‹‹ "текст";


Что делать с пустыми группами?

Допустим, ввод не logger.logf("Some %s text", port);, а logger.logf("Some %s", port);. Результат будет таким:

logger() ‹‹ "Некоторый " ‹‹ порт ‹‹ "";

Вам придется удалить << "", чтобы получить что-то чистое.

person Thomas Ayoub    schedule 08.07.2016