Usunąć określony znak z następnego wiersza w tej samej pozycji?

3

Załóżmy, że mam dane dwóch linii o tej samej długości

abcdb#lae#blabl#a
abc~bola~xblabl~a

Muszę usunąć #znak w pierwszym wierszu (może być jeden lub wiele # w pierwszym wierszu), a także znak w tym samym miejscu w następnym wierszu, aby dane stały się

abcdblaeblabla
abc~bla~blabla

Próbowałem, sed '/#/{n;s/~//g}'ale usuwa więcej znaków, niż chcę.

Jason Qin
źródło
Dziękuję Ci! W pierwszym wierszu może znajdować się jeden lub wiele #. Zredagowałem swoje pytanie
Jason Qin

Odpowiedzi:

2

awk

Metody te powtarzają się dla każdej pary linii (1 i 2; 3 i 4; itd.), Pracując dla tylu #znaków, ile jest w pierwszym wierszu każdej pary i zakładając, że dwie linie każdej pary mają tę samą długość.

Kompatybilny z GNU awk (Linux) i BSD awk (Mac).


Za pomocą podciągów:

awk '{ a=$0 ; gsub(/#/,"",$0) ; print $0 ; getline ; for (n=1;n<=length(a);n++) if ( substr(a,n,1) != "#" ) printf "%s",substr($0,n,1) ; printf "%s",RS }' file.txt

Ten sam kod, sformatowany dla węższych ekranów:

awk '{
  a=$0 ;
  gsub(/#/,"",$0) ;
  print $0 ;
  getline ;
  for (n=1;n<=length(a);n++)
    if ( substr(a,n,1) != "#" )
      printf "%s",substr($0,n,1) ;
  printf "%s",RS
  }' file.txt
  • a=$0
    Zapisz kopię pierwszego wiersza.
  • gsub(/#/,"",$0) ; print $0
    Usuń wszystko #z pierwszego wiersza (nie z kopii), a następnie wydrukuj zmodyfikowany pierwszy wiersz.
  • getline
    Przejdź do następnej linii.
  • for (n=1;n<=length(a);n++)
    Przechodź przez każdy znak kopii pierwszego wiersza.
    • if ( substr(a,n,1) != "#" )
      Jeśli ten jednoznakowy podciąg nie jest #
      • printf "%s",substr($0,n,1)
        … Następnie wydrukuj znak z odpowiedniej pozycji w drugim wierszu.
  • printf "%s",RS
    Zakończ drugą linię znakiem nowej linii.

Korzystanie z tablic:

awk '{ c=d="" ; elements=split($0,a,"") ; getline ; split($0,b,"") ; for (n=1;n<=elements;n++) if (a[n]!="#") { c = c a[n] ; d = d b[n] } ; print c ; print d }' file.txt

Przeformatowany dla węższych ekranów:

awk '{
  c=d="" ;
  elements=split($0,a,"") ;
  getline ;
  split($0,b,"") ;
  for (n=1;n<=elements;n++)
    if (a[n]!="#")
      { c = c a[n] ; d = d b[n] } ;
  print c ;
  print d
  }' file.txt
  • c=d=""
    Zainicjuj dwa puste ciągi. Będą to zmodyfikowane wersje dwóch linii wejściowych. Ten krok jest konieczny, jeśli są więcej niż dwa wiersze wprowadzania.
  • elements=split($0,a,"")
    Przekształć pierwszy wiersz danych wejściowych w tablicę, z jednym znakiem na element tablicy. Przechowuj liczbę elementów tablicy jako zmienną elements.
  • getline
    Przejdź do następnej linii.
  • split($0,b,"")
    Przekształć drugi wiersz danych wejściowych w tablicę, z jednym znakiem na element tablicy.
  • for (n=1;n<=elements;n++)
    Przejdź przez każdy element tablicy pierwszego wiersza.
    • if (a[n]!="#")
      Jeśli ten element tablicy jednoznakowej nie jest #
      • { c = c a[n] ; d = d b[n] }
        … Następnie, dla każdej z dwóch linii, zachowaj znak z pozycji n.
  • print c ; print d
    Wydrukuj nowe wersje dwóch linii.

Uwaga: Wersja awk dla komputerów Mac (BSD) nie obsługuje automatycznie elementów tablicy w kolejności numerycznej. To początkowo dało mi zaskakujące wyniki.

Kolejność, w jakiej pętla „for (indx in array)” przemierza tablicę, jest niezdefiniowana w POSIX awk i różni się w zależności od implementacji. gawk pozwala kontrolować kolejność, przypisując specjalne predefiniowane wartości do PROCINFO ["sorted_in"].

- Podręcznik użytkownika GNU Awk

Elementy są nadal ponumerowane 1,2,3,...w momencie tworzenia split, podobnie jak w GNU awk, ale awk BSD niekoniecznie widzi je w tej kolejności podczas używania for (n in array). W ten sposób dostaniesz bełkot.

Aby obejść ten problem, możesz zapisać długość tablicy (liczbę elementów) podczas tworzenia tablicy - np. elements=split($0,a,"")- a następnie iterować elementy za pomocą for (n=1;n<=elements;n++), tak jak to zrobiłem tutaj.


Przykładowe wejście ( file.txt):

abcdb#lae#blabl#a
abc~bola~xblabl~a
#alpha#beta#gamma#delta#epsilon#
abcdefghijklmnopqrstuvwxyzabcdef

Przykładowe dane wyjściowe:

abcdblaeblabla
abc~bla~blabla
alphabetagammadeltaepsilon
bcdefhijkmnopqstuvwyzabcde
Gaultheria
źródło
2

Możesz to zrobić za pomocą sed w następujący sposób. Umieść dwa znaczniki na początku dwóch linii, po umieszczeniu obu w przestrzeni wzorów.

Następnie zacznij przesuwać je w prawo po jednej postaci. Podczas tego ruchu zwróć uwagę na to, co leży bezpośrednio na prawo od markerów i podejmij odpowiednie działania.

Zatrzymaj się, gdy znacznik dotrze do końca obszaru wzoru. Teraz zdejmij znaczniki, gdy ich praca zostanie wykonana, a to, czego chcesz, to czego chcesz. Uwaga: znacznikiem jest \ n

 sed -Ee '
   /#/N;/\n/!b
   s/\n/&&/;s/^/\n/
   :a
       /\n#(.*\n.*\n)./{
          s//\n\1/;ba
       }
      s/\n(.)(.*\n.*)\n(.)/\1\n\2\3\n/
   /\n$/!ba
   s/\n//;s///2
'    input

Korzystanie z Perla jest rozwiązywane według następujących zasad:

 perl -pe  ' 
     next unless /#/;

     my($n,$p) = (scalar <>);

     while ( /#/g ) {
        pos($n) = pos() - 1 - $p++;
        $n =~ s/\G.//;
     }

     y/#//d;s/\z/$n/;
'      input_file 

Pracujący:

1. Skip lines that donot have hash char.
 2. Save the next line in $n and init. $p counter which keeps track of the number of hash chars erased till now.
3.  Monitor the position of the hash char in a while loop and using info generate the position of the char to be deleted in next line.
4.  Erase it using the \G metachar in s///
5.  In the final step remove the hash chars from present line and append the next line to it.

Pokazana jest inna metoda, tym razem wykorzystująca tablice:

perl -aF'' -ne '
    print,next unless /#/;
    print,last if eof;

    my @I = grep { $F[$_] ne "#" } 0 .. $#F;
    my @N = split //, <>;

    print @F[@I], @N[@I];
'    input_file

Pracujący:

1. Invoke Perl to split each line on a per character basis and have it stored in the array @F anew for every line read.
2.  Record the array indices for which the array element is a non hash character.
3.  Readin the next line, split it on a per character basis and store in array @N.
4. Now its a matter of selecting the indices we stored in @I and fetch those from arrays @F and @N.

Metoda wyrażeń regularnych:

perl -pe '
   $_ .= <> unless eof;

    s/\G.(.*\n.{@{[+pos]}})./$1/ while /(?=#.*\n.)/g;
'        input_file

Opis:

° dołącz kolejną linię do prądu, o ile nie jest to ostatnia linia.

° Zapisz pozycje znaków skrótu w pierwszym wierszu za pomocą pętli while.

° Następnie usuń znak krzyżyka z pierwotnego wiersza i znak z odpowiedniej pozycji w następnym wierszu.

° Po zakończeniu pętli while opcja - p automatycznie wydrukuje $ _ na standardowe wyjście.

Metoda z operacjami zwykłego łańcucha:

perl -pe '
   last if eof;
   my $n = <>;
   while ( (my $p = index($_,"#")) > -1 ) {
      substr($_, $p, 1) = "" for $_, $n;
   }
   $_ .= $n;
'       input_file

Obejmuje to użycie wbudowanego indeksu do sprawdzenia pozycji skrótu, a następnie użycie tego w wbudowanym substracie dwa razy ... w pierwszej i następnej linii.

Rakesh Sharma
źródło
0

Jest to dość łatwe awk. Kiedy zobaczysz a #, określ, gdzie jest w linii. Następnie dla tej linii i wszystkich kolejnych linii wytnij tę pozycję znaku z linii.

awk '
    /#/ { pound=index($0, "#") }
        {
                if (pound)
                        print substr($0, 1, pound-1) substr($0, pound+1)
                else
                        print
        }
    '
G-Man
źródło
1
Pozwala tylko usunąć pierwszy #, prawda? Przepraszamy, zaktualizowałem moje pytanie i w pierwszym wierszu może być wiele #.
Jason Qin
0
awk '{gsub(/#/,"")sub(/bola~x/,"bla~")sub(/~a$/,"a")}1' file

output
abcdblaeblabla
abc~bla~blabla
Claes Wikner
źródło
Jeśli zrobiłem coś złego, proszę powiedz mi, co to jest!
Claes Wikner,
0

Z gnu awk za pomocą gensub

awk '
/#/{
  a=$0
  b=length()
  getline
  $0=a RS$0
  while($0!=a){
    a=$0
    $0=gensub("([^#]*)#(.{"b--"}).","\\1\\2",1)}
}1' infile

Wyjaśnić :

/ # /: dla każdej linii z #

a = 0 USD: zapisz wiersz w

b = długość (): pobierz długość w b

getline: pobierz następną linię

0 USD = RS 0 USD: dodaj poprzednią linię zapisaną na początku bufora 0 USD, a następnie RS separator rekordów

Teraz 0 $ zawiera 2 linie

while ($ 0! = a): podczas gdy linia przechowywana w a różni się od bufora 0 $

a = 0 USD: pobierz bufor 0 USD w

0 $ = gensub ("([^ #] *) # (. {" B - "}).", "\\ 1 \\ 2", 1): usuń pierwszy # w $ 0 i odpowiedni znak w druga linia

W tym samym czasie zmniejszenie (b--) długości pierwszego wiersza o 1, ponieważ 1 # został usunięty

1: gdy nie ma już # w pierwszym wierszu, wydrukuj 0 USD

ctac_
źródło