Exclui determinado caractere da próxima linha na (s) mesma (s) posição (ões)?

3

Suponha que eu tenha os dados de duas linhas com o mesmo comprimento

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

Eu preciso remover o # na primeira linha (pode haver um ou muitos # na primeira linha) e também o caractere no mesmo local na próxima linha, para tornar os dados

abcdblaeblabla
abc~bla~blabla

Eu tentei sed '/#/{n;s/~//g}' , mas ele remove mais caracteres do que eu quero.

    
por Jason Qin 28.04.2018 / 05:00

5 respostas

2

awk

Esses métodos se repetem para cada par de linhas (1 e 2; 3 e 4; etc), trabalhando para quantos caracteres # , como na primeira linha de cada par, e assumindo que as duas linhas de cada linha par são do mesmo comprimento.

Compatível com o GNU awk (Linux) e o BSD awk (Mac).

Uso de substrings:

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

O mesmo código, reformatado para telas mais estreitas:

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
    Salve uma cópia da primeira linha.
  • gsub(/#/,"",$0) ; print $0
    Exclua todos os # da primeira linha (não da cópia) e imprima a primeira linha modificada.
  • getline
    Vai para a próxima linha.
  • for (n=1;n<=length(a);n++)
    Passe por cada caractere da cópia de primeira linha.
    • if ( substr(a,n,1) != "#" )
      Se esta subcadeia de caractere único não for # ,…
      • printf "%s",substr($0,n,1)
        … imprima o caractere da posição correspondente na segunda linha.
  • printf "%s",RS
    Encerra a segunda linha com um caractere de nova linha.

Usando matrizes:

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

Reformatado para telas mais estreitas:

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=""
    Inicializa duas strings em branco. Estas serão versões modificadas das duas linhas de entrada. Esta etapa é necessária se houver mais de duas linhas de entrada.
  • elements=split($0,a,"")
    Converte a primeira linha de entrada em uma matriz, com um caractere por elemento da matriz. Armazene o número de elementos da matriz como a variável elements .
  • getline
    Vai para a próxima linha.
  • split($0,b,"")
    Converte a segunda linha de entrada em uma matriz, com um caractere por elemento de matriz.
  • for (n=1;n<=elements;n++)
    Passe por cada elemento da matriz de primeira linha.
    • if (a[n]!="#")
      Se este elemento de matriz de caractere único não for # ,…
      • { c = c a[n] ; d = d b[n] }
        … então, para cada uma das duas linhas, reter o caractere da posição n .
  • print c ; print d
    Imprima as novas versões das duas linhas.

Cuidado: A versão do awk no Mac (BSD) não manipula automaticamente os elementos da matriz em ordem numérica. Isso inicialmente me deu resultados surpreendentes.

The order in which a ‘for (indx in array)’ loop traverses an array is undefined in POSIX awk and varies among implementations. gawk lets you control the order by assigning special predefined values to PROCINFO["sorted_in"].

The GNU Awk User’s Guide

Os elementos ainda são numerados 1,2,3,... no momento da criação com split , como no GNU awk, mas o BSD awk não os vê necessariamente nessa ordem ao usar for (n in array) . Assim, você vai ter rabiscos sem sentido.

Para contornar isso, você pode armazenar o tamanho de um array (número de elementos) ao criar o array - por exemplo, elements=split($0,a,"") - e percorrer os elementos usando for (n=1;n<=elements;n++) , como fiz aqui.

Exemplo de entrada ( file.txt ):

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

Exemplo de saída:

abcdblaeblabla
abc~bla~blabla
alphabetagammadeltaepsilon
bcdefhijkmnopqstuvwyzabcde
    
por 28.04.2018 / 08:27
2

Você pode fazer isso com sed da seguinte maneira. Coloque dois marcadores no início das duas linhas, depois de colocar ambos no espaço padrão.

Em seguida, comece a movê-los para a direita, um caractere por vez. Durante este movimento, observe o que está imediatamente à direita dos marcadores e aja de acordo.

Pare quando o marcador atingir o final do espaço padrão. Agora retire os marcadores enquanto seu trabalho é feito e o que resta é o que você quer. Observe que o marcador é \ n

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

Usando o Perl, ele é abordado da seguinte maneira:

 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 

Trabalhando:

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.

Outro método, desta vez usando matrizes, é mostrado:

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

Trabalhando:

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.

Método de expressões regulares:

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

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

Descrição:

° acrescente a próxima linha à corrente, desde que não seja a última linha.

° Grave as posições dos caracteres hash na primeira linha por meio de loop while.

° Em seguida, remova o caractere de hash na linha original e o caractere na posição correspondente na próxima linha.

° Depois de estarmos fora do loop while, a opção -p imprimirá automaticamente o $ _ para stdout.

Método com operações de string simples:

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

Isso envolve o uso do índice embutido para verificar a posição do hash e usá-lo no substrato integrado duas vezes ... nas primeiras linhas e nas próximas linhas.

    
por 28.04.2018 / 07:26
0

Isso é bastante fácil em awk . Quando você vir um # , determine onde está a linha. Então, para essa linha e todas as linhas seguintes, cortar essa posição de personagem fora da linha.

awk '
    /#/ { pound=index($0, "#") }
        {
                if (pound)
                        print substr($0, 1, pound-1) substr($0, pound+1)
                else
                        print
        }
    '
    
por 28.04.2018 / 05:49
0
awk '{gsub(/#/,"")sub(/bola~x/,"bla~")sub(/~a$/,"a")}1' file

output
abcdblaeblabla
abc~bla~blabla
    
por 28.04.2018 / 18:39
0

Com o gnu awk com o uso do gensub

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

Explique:

/ # /: para cada linha com #

a = $ 0: salvar linha em um

b = length (): obtém o comprimento em b

getline: obtenha a próxima linha

$ 0 = a RS $ 0: adicione a linha anterior armazenada em um no início do buffer $ 0 seguido por RS o separador de registro

Agora, $ 0 contém 2 linhas

while ($ 0! = a): enquanto a linha armazenada em a é diferente do buffer $ 0

a = $ 0: obtenha o buffer $ 0 em um

$ 0 = gensub ("([^ #] *) # (. {" b - "}).", "\\ 1 \\ 2", 1): remova o primeiro # em $ 0 e o correspondente char na segunda linha

Ao mesmo tempo, diminua (b--) o comprimento da primeira linha por 1, porque 1 # foi removido

1: quando não há mais # na primeira linha imprima $ 0

    
por 28.04.2018 / 21:34