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?

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

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


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 .


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.