Jak rekurencyjnie zamieniać postacie na sed?

13

Czy można rekurencyjnie zastępować wystąpienia sekwencji znaków bez powtarzania tej samej sekwencji?

Wykonując sedjak w poniższych scenariuszach, mogę uzyskać wspomniane dane wyjściowe.

$ 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  

Jednak oczekuję, że dane wyjściowe będą wyglądać następująco.

Wejście:

XX
XXX
XXXX

Oczekiwany wynik:

XoX
XoXoX
XoXoXoX

Czy możliwe jest osiągnięcie oczekiwanego zachowania przy pomocy samego seda?

Ishan Madhusanka
źródło

Odpowiedzi:

24

Możesz to zrobić:

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

Z:

  • -e ':loop' : Utwórz etykietę „pętli”
  • -e 't loop' : Przejdź do etykiety „pętli”, jeśli poprzednie zastąpienie zakończyło się pomyślnie
Gohu
źródło
10

W tym konkretnym przypadku przydatne byłoby spojrzenie w przyszłość lub w przeszłość. Myślę, że GNU sedich nie obsługuje. Z perl:

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

Możesz także użyć lookbehind i lookahead, takich jak:

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

Gdzie:

(?<=X)jest pozytywnym wyglądem, twierdzenie o zerowej długości, które upewniające się, że mamy X przed bieżącą pozycją,
(?=X)jest spojrzeniem pozytywnym, stwierdzenie o zerowej długości, które zapewnia, że ​​mamy X za bieżącą pozycją

Używanie w Perl One-Liner:

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

Gdzie:

-p powoduje, że Perl zakłada pętlę wokół programu z niejawnym drukowaniem bieżącej linii

Toto
źródło
5

Zapętlona odpowiedź jest ogólnym sposobem robienia tego, o co prosisz.

Jednak w przypadku danych, zakładając, że używasz GNU, możesz po prostu:

sed 's/\B/o/g'

\bI \Bopcje są regex rozszerzenia :

  • \b dopasowuje granice słów, tj. przejście ze znaku „słowo” na znak „inny niż słowo” lub odwrotnie
  • \Bpasuje do przeciwieństwa \b. tzn. luki „w środku” słów. To pozwala nam wstawiać znaki wewnątrz słowa, ale nie na zewnątrz, zgodnie z wymaganiami.

Wypróbuj online .

Zakłada się, że znaki wejściowe są w rzeczywistości wszystkimi znakami „słownymi”.


Alternatywnie, jeśli nie masz GNU sed lub jeśli znaki wejściowe nie są wszystkimi znakami „słownymi”, nadal możesz osiągnąć swój cel bez zapętlania:

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

To po prostu umieszcza oznak po każdym znaku, a następnie usuwa finał oz łańcucha.

Wypróbuj online .

Cyfrowa trauma
źródło
1
Zakłada się, że łańcuchy wejściowe składają się z pewnej liczby Xi nic więcej. Oba rozwiązania zawodzą, jeśli obecne są inne postacie ...
AnoE
@AnoE W drugiej próbce jest to naprawione przez proste zastąpienie Xprzez .. Proszę zobaczyć edycję.
Cyfrowa trauma
Nie jest to odpowiednik przypadku podanego przez PO. Podał dokładnie te potrzebne RE (zmień wystąpienia XX w ciągu). Twoje wersje dają tylko ten sam wynik co jego dokładnie te same ciągi wejściowe, które podał; nie dla ogólnych ciągów wejściowych.
AnoE
4

Sprawdziłem, czy jest jakaś flaga, aby tak się stało.
Nawet jeśli takie zachowanie istniało, będzie to bardzo pochłaniać zasoby.

Jednak w tym konkretnym przypadku użycia wyrażenie może mieć tylko dwa razy wyrażenie i osiągnąć wymaganą funkcjonalność. tj. z 2 powtarzającymi się sedwyrażeniami.

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
Ishan Madhusanka
źródło