13

Можно ли заменить вхождения последовательности символов рекурсивно, не повторяя ту же последовательность снова?

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

$ echo XX | sed -e 's/XX/XoX/g'
XoX  
$ echo XXX | sed -e 's/XX/XoX/g'
XoXX  
$ echo XXXX | sed -e 's/XX/XoX/g'
XoXXoX  

Тем не менее, я ожидаю, что результат будет соответствовать следующему.

Входные данные:

XX
XXX
XXXX

Ожидаемый результат:

XoX
XoXoX
XoXoXoX

Можно ли достичь ожидаемого поведения с помощью одного только sed?

4 ответа4

23

Ты можешь сделать:

> echo XXXX | sed -e ':loop' -e 's/XX/XoX/g' -e 't loop'
XoXoXoX

С:

  • -e ':loop' : создать метку "loop"
  • -e 't loop' : перейти к метке "loop", если предыдущая замена прошла успешно
10

В этом конкретном случае было бы полезно забегать вперед или оглядываться назад. Я думаю, что GNU sed не поддерживает их. С perl:

perl -ne 's/X(?=X)/Xo/g; print;'

Вы также можете использовать lookbehind и lookahead, например:

s/(?<=X)(?=X)/o/g

Куда:

(?<=X) является положительным взглядом сзади, утверждением нулевой длины, которое гарантирует, что у нас есть X перед текущей позицией
(?=X) является положительным прогнозом, утверждением нулевой длины, которое гарантирует, что у нас есть X после текущей позиции

Использование в perl однострочном:

perl -pe 's/(?<=X)(?=X)/o/g' inputfile

Куда:

-p заставляет Perl предполагать цикл вокруг программы с неявным выводом текущей строки

5

Циклический ответ - это общий способ сделать то, что вы просите.

Однако в случае ваших данных, если вы используете GNU, вы можете просто сделать:

sed 's/\B/o/g'

Опции \b и \B являются расширениями регулярных выражений:

  • \b соответствует границам слова, т.е. переходу от символа "слово" к символу "не слово", или наоборот
  • \B соответствует противоположности \b . то есть пробелы "внутри" слова. Это позволяет нам вставлять символы внутри слова, но не снаружи, как требуется.

Попробуйте онлайн.

Это предполагает, что входные символы на самом деле являются символами "слова".


В качестве альтернативы, если у вас нет GNU sed или если входные символы не все "слово", вы все равно можете достичь своей цели без зацикливания:

sed 's/./&o/g;s/o$//'

Это просто помещает o после каждого символа, а затем удаляет последний o из строки.

Попробуйте онлайн.

4

Я проверил, есть ли какой-нибудь флаг, чтобы это произошло.
Даже если бы такое поведение было там, оно будет очень ресурсоемким.

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

echo XX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'     # outputs XoX
echo XXX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'    # outputs XoXoX
echo XXXX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'   # outputs XoXoXoX

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .