Como transpor um arquivo de texto em caráter base

1

Existem algumas ferramentas como datamesh para transpor uma matriz em um arquivo csv , mas eu quero trocar caracteres com base em caracteres de linhas e colunas. Então um arquivo

abcde
fghij
klmn
opqrs

deve se tornar

afko
bglq
chmq
dinr
ej s

Note que como a linha 3 é mais curta, um espaço em branco deve ser inserido na última linha.

Do código-fonte, eu poderia escrever algum programa em C para fazer isso, mas achei que uma vez encontrei uma ferramenta para fazer isso, mas meu mecanismo de pesquisa não me ajuda a encontrá-lo.

    
por Philippos 01.08.2017 / 15:54

3 respostas

3

Você pode fazer isso com o utilitário rs no modo de transposição pura ( -T ) - se você espaçar a entrada apropriadamente primeiro, por exemplo,

$ sed -e 's/./& /g' -e 's/ $//' file
a b c d e
f g h i j
k l m n
o p q r s

(ou, se você tem o GNU sed, você pode usar sed 's/./ &/2g' ; outra opção está usando um loop sed -E ':a; s/([^ ])([^ ])/ /; ta' ); então

$ sed -e 's/./& /g' -e 's/ $//' file | rs -Tng0
afko
bglp
chmq
dinr
ej s

As opções importantes são:

  • -T pure transpose
  • -n pad entradas nulas
  • -g0 define a largura da medianiz de saída (espaçamento entre colunas) como zero

Como alternativa, fazer a divisão de entrada usando awk com um separador de campo de entrada vazio e um separador de campo de saída padrão:

awk '{$1=$1} 1' FS= file | rs -Tng0
    
por 01.08.2017 / 19:01
2

Uma solução geral para transposição com awk segue.

Para funcionar corretamente, precisamos do número de colunas. Isso pode ser encontrado ao ler o arquivo em uma matriz de valores:

#!/bin/bash
file=i4
delimiter=""
sep=""

transpose() { : # comment sed for newer awks.
              # Do this to separate characters in quite old awk
              # very old wak does not allow that the FS could be Null.
              #sed -e 's/./ &/g' "$file" |
              awk ' 
                   { for(i=1;i<=NF;i++){a[NR,i]=$i};{(NF>m)?m=NF:0} }
                   END { for(j=1; j<=m; j++)
                         { for(i=1; i<=NR; i++)
                           { b=((a[i,j]=="")?" ":a[i,j])
                             printf("%s%s",(i==1)?"":sep,b)
                           }
                           printf("\n")
                         }
                       }
                   ' FS="$delimiter" sep="$sep" cc="$countcols" <"$file"
             }

transpose

Com este arquivo:

abc
fghij
klmn
opqrs

imprimirá:

afko
bglp
chmq
 inr
 j s

O Awk se encarrega de separar os caracteres se o "separador de campos" for nulo.
Os caracteres são impressos em uma linha se a variável sep também for nula.

Se o awk disponível for mais antigo, um FS nulo não é válido. Use os dois comandos a seguir.

Para contar o número de caracteres, use isso em awks mais antigos:

# Work with any POSIX awk to find the max character count in all rows.
countcols=$(awk '{l=length($0);(l>max)?max=l:0}END{print max}' < "$file")

Para fazer a transposição, um espaço poderia ser adicionado na frente de cada personagem e usar um espaço como um "separador de campo" e evitar o FS vazio:

sed -e 's/./ &/g' < "$file" |
awk ' {for(i=1;i<=cc;i++){if($i==""){$i=" "};r[i]=r[i]sep$i;};sep=""};
      END{for(i=1;i<=cc;i++)print(r[i])}
    ' cc="$countcols"

Comente a linha sed para os novos awks.

    
por 02.08.2017 / 00:29
0

Aqui está uma solução com cut e paste . Como você não tem nenhum delimitador como espaço ou tabulador, ele precisa de alguma correção com sed :

for COL in {1..5}; do cut -c $COL < infile | paste -s -d_ ; done | sed -e 's/__/_ /g' -e 's/_//g'

Aqui dividido em várias linhas:

for COL in {1..5}; do
  cut -c $COL < infile | paste -s -d_
done | sed -e 's/__/_ /g' -e 's/_//g'

A saída da primeira parte se parece com:

for COL in {1..5}; do cut -c $COL < infile | paste -s -d_ ; done

a_f_k_o
b_g_l_p
c_h_m_q
d_i_n_r
e_j__s

Uma coisa chata é que você precisa saber quantas colunas existem antes de começar.

    
por 01.08.2017 / 16:47