É possível com o Gedit ou a linha de comando modificar cada quarta linha de um arquivo de texto?

11

Estou tentando converter um arquivo de texto em uma planilha separada por tabulação. Meu arquivo de texto é algo como isto:

Dog
Cat
Fish
Lizard
Wolf
Lion
Shark
Gecko
Coyote
Puma
Eel
Iguana

Com as funções de pesquisa e substituição padrão no Gedit ou no LibreOffice, é fácil substituir o final da linha por uma guia. Mas se eu apenas trocar retornos de carro por abas, vou receber o seguinte:

Dog   Cat   Fish   Lizard   Wolf   Lion   Shark   Gecko   Coyote   Puma   Eel   Iguana

Mas o que eu preciso fazer é que fique assim:

Dog   Cat   Fish   Lizard
Wolf   Lion   Shark   Gecko  
Coyote   Puma   Eel   Iguana

Então, posso trocar todos os caracteres de fim de linha por uma guia, exceto a cada quarta linha?

Não sei se esse tipo de iteração condicional pode ser feita com expressões regulares dentro de um programa como o Gedit ou o LibreOffice, então talvez isso precise ser algum tipo de função de linha de comando? Nem sei ao certo qual é a melhor ferramenta para começar.


Atualizar:

Eu tentei os seguintes comandos:

sed 'N;N;N;s/\n/\t/g' file > file.tsv

paste - - - - < file > file.tsv

pr -aT -s$'\t' -4 file > file.tsv

xargs -d '\n' -n4 < inputfile.txt

Mas quando tento abrir o tsvarquivo resultante no LibreOffice, as colunas não estão certas. Não tenho certeza se isso significa que não estou executando os comandos acima corretamente ou se estou fazendo algo errado na função de importação do LibreOffice:

Abertura do TSV no Calc

Apenas para referência, o resultado desejado deve ser assim:

Colunas adequadas

Questionador
fonte

Respostas:

16

Você pode usar um editor de linha de comando comosed

sed 'N;N;N;s/\n/\t/g' file > file.tsv

ou, de forma mais programática, adicionando caracteres de continuação de linha de barra invertida a cada uma das linhas que você deseja unir usando o n skip moperador de endereço do GNU sed e seguindo-o com o one-liner clássico para unir linhas contínuas:

sed '0~4! s/$/\t\\/' file | sed -e :a -e '/\\$/N; s/\\\n//; ta'

Veja, por exemplo, Sed One-Liners Explained :

  1. Anexe uma linha à próxima se terminar com uma barra invertida "\".

    sed -e :a -e '/\\$/N; s/\\\n//; ta'
    

No entanto, o IMHO seria mais fácil com um dos outros utilitários de processamento de texto padrão, por exemplo

paste - - - - < file > file.tsv

(o número de -corresponderá ao número de colunas) ou

pr -aT -s$'\t' -4 file > file.tsv

(você pode omitir a opção -s$'\tse não se importar que a saída seja separada por várias guias).


O estranho comportamento de reimportação que você está observando é quase certamente porque o arquivo original possui terminações de linha CRLF no estilo Windows. Se você precisar trabalhar com arquivos do Windows, poderá rolar a conversão no comando de várias maneiras, por exemplo

tr -d '\r' < file.csv | paste - - - -

ou

sed 'N;N;N;s/\r\n/\t/g' file.csv

O primeiro removerá TODOS os retornos de carro, enquanto o último preservará um CR no final de cada uma das novas linhas (que pode ser o que você deseja se o usuário final pretendido estiver no Windows).

chave de aço
fonte
1
Uma observação sobre as terminações de linha no estilo Windows: as ferramentas padrão para converter entre elas e o estilo Unix são dos2unixe unix2dos.
David Foerster
13

Você pode usar xargssempre para agrupar quatro linhas em uma, separadas com um único espaço cada:

xargs -d '\n' -n4 < inputfile.txt

-d '\n'define o delimitador de entrada como um caractere de nova linha, caso contrário, também seria interrompido nos espaços. Se você tiver apenas uma palavra por linha de entrada, poderá omitir isso.
-n4define o número do argumento (o número de itens de entrada por linha de saída) como 4.

Resultado:

Dog Cat Fish Lizard
Wolf Lion Shark Gecko
Coyote Puma Eel Iguana

Ou, se você quiser guias como separadores em vez de um espaço, poderá substituí-las posteriormente. No entanto, se você tivesse espaços em suas linhas de entrada, eles também seriam substituídos:

xargs -d '\n' -n4 | tr ' ' '\t'

Saída (aparência dependendo da largura da guia do navegador / terminal):

Dog Cat Fish    Lizard
Wolf    Lion    Shark   Gecko
Coyote  Puma    Eel Iguana
Byte Commander
fonte
Este método tem o benefício de se comportar razoavelmente, mesmo quando o número total de linhas de entrada não é um múltiplo de quatro.
Elias Kagan
3

Você também pode usar:

awk -v ORS="" '{print $1; print NR%4==0?"\n":"\t"}' file > file.tsv 

As duas variáveis ​​internas do awk são:

  • ORS: O utput R ecord S eparator (padrão = nova linha). É adicionado no final de cada comando de impressão.
  • NR: N úmero da corrente de R ow awk está a processar.

Este comando, para cada linha, exibirá o conteúdo da primeira coluna (e somente aqui). Em seguida, escolha adicionar uma nova linha ou uma guia testando o restante da divisão de NRpor 4.

arauk
fonte
3

Outra awkabordagem mais curta :

awk '{printf $0 (NR%4?"\t":"\n")}' infile

Este printf a apenas uma coluna seguido pelo próximo e no próximo e ... e um Tab \tpersonagem depois de cada, mas vai printf um \ncaráter ewline quando N úmero de R ecord foi fator de 4 (onde NR%4retornará 0 (false) que é o Operador Ternário condition(s)?when-true:when-falseestá fazendo.)

αғsнιη
fonte
3

Minha solução para isso seria usar a combinação de sede sed. Primeiro, você pode marcar cada quarta linha com algum caractere especial, por exemplo >, usando esta solução:

Nesse caso, você deseja começar da linha 5 e marcar a cada quarta linha depois dela. No GNU sedisso pode ser dado como um endereço 5~4. Você pode usar este comando:

sed '5~4s/^/>/' file1 > file2

Então você precisa remover as novas linhas, o que pode ser feito com um sedloop:

sed ':a;N;s/\n/ /;ba' file2 > file3

Existem maneiras mais fáceis de converter novas linhas em outro caractere, por exemplo, com tr:

tr '\n' ' ' < file2 > file3

De qualquer maneira, combinar os dois dá

Dog   Cat   Fish   Lizard   >Wolf   Lion   Shark   Gecko   >Coyote   Puma   Eel   Iguana

(a sedversão deixa uma nova linha à direita, enquanto a trversão não)

Depois disso, você só precisa converter os caracteres especiais inseridos em novas linhas; veja, por exemplo, Converter um arquivo delimitado por tabulação para usar novas linhas . Nesse caso, altere >para novas linhas:

sed 'y/>/\n/' file3 > outfile

O ycomando executa a mesma função que tr, transformando um caractere em outro, mas você pode usar o scomando aqui igualmente bem. Com s, você precisa goperar em cada partida da linha ( sed 's/>/\n/g').

Em vez de criar dois arquivos intermediários, você pode usar pipes:

$ sed '5~4s/^/>/' file | sed ':a;N;s/\n/ /;ba' | sed 'y/>/\n/'
Dog Cat Fish Lizard 
Wolf Lion Shark Gecko 
Coyote Puma Eel Iguana

Se os espaços à direita forem um problema, você poderá adicionar outro comando para removê-los:

| sed 's/ $//'
spaceman117X
fonte
2

Por uma questão de "integridade", aqui está uma solução pura do bash:

#!/usr/bin/env bash

sep=$'\t'

while read one \
      && read two \
      && read three \
      && read four
do
  printf "%s\n" "$one$sep$two$sep$three$sep$four"
done

Funciona também com espaços, supondo que IFSesteja definido corretamente (o que deveria, por padrão, AFAIK). Além disso, acho que isso pode até ser um script de shell portátil e funcionar com qualquer shell compatível com POSIX.

Daniel Jour
fonte
1
Isso não é portátil para shells compatíveis com POSIX em geral, porque a $' 'forma de citação não é requerida pelo POSIX. Por exemplo, em dash(que fornece shpor padrão no Ubuntu), executando printf '%s\n' $'a\tb'apenas saídas $a\tb. Isso não significa que isso não seja útil; funciona na festança. No entanto, como em algumas das outras soluções postadas pelas pessoas, produz saída incompleta se o número de linhas de entrada não for múltiplo de quatro. Além disso, recomendo o uso read -r, pois não há razão para pensar que a expansão de barras invertidas escape no arquivo de entrada seja desejada aqui.
Elias Kagan
Você poderia simplesmente fazerprintf '%s\t%s\t%s\t%s\n' "$one" "$two" "$three" "$four"
terdon
2

Uma macro vim (gravada com q) pode aplicar sua operação e pular três linhas. Depois, basta executar essa macro n vezes.

por exemplo:

qq $ J i <TAB> <ESC> $ J i <TAB> <ESC> $ J i <TAB> <ESC> ^^ j qq 100 @q
rackandboneman
fonte
2

Como você pediu uma solução Gedit, algo como isto deve funcionar:

Encontrar:

(\w+)[\r\n]+(\w+)[\r\n]+(\w+)[\r\n]+(\w+)[\r\n]+

Substituir com:

\1\t\2\t\3\t\4\n

Verifique se a caixa de seleção para expressões regulares está marcada.

Como funciona:

A primeira etapa é encontrar uma série de caracteres da palavra, com \ w +, e capturar os resultados na variável \ 1, envolvendo parênteses em torno da expressão:

(\w+)

Em seguida, procuramos uma série de caracteres de final de linha, \ re \ n, ou CR e LF. Como os arquivos formatados do Windows usam os dois, criamos uma classe de caracteres colocando esses dois caracteres entre colchetes. O plus faz com que procure um ou mais caracteres:

[\r\n]+

Finalmente, repetimos isso mais três vezes, armazenando cada palavra subsequente nas variáveis ​​\ 2, \ 3 e \ 4. Isso torna nossa substituição pela expressão simples. Nós apenas precisamos colocar caracteres de tabulação, \ t, e um novo caractere de linha, \ n, nos locais apropriados para a formatação necessária.

Jason Wood
fonte