Como converter multilinhas em singleline, mas preservar parágrafos

0

Digamos que eu tenha um monte de texto (markdown) com cada frase em uma linha separada (para facilitar o controle de versão em caso de erros de digitação). Exemplo file.txt :

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Dictum sit amet justo donec enim diam vulputate.
Nunc faucibus a pellentesque sit amet.

Quis enim lobortis scelerisque fermentum dui faucibus in.
Leo duis ut diam quam nulla porttitor massa id neque.
Vitae tortor condimentum lacinia quis vel eros.

Como posso converter cada parágrafo em uma única linha para que pareça:

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Dictum sit amet justo donec enim diam vulputate. Nunc faucibus a pellentesque sit amet.

Quis enim lobortis scelerisque fermentum dui faucibus in. Leo duis ut diam quam nulla porttitor massa id neque. Vitae tortor condimentum lacinia quis vel eros. Velit euismod in pellentesque massa placerat duis ultricies lacus.

Minha idéia é encontrar e substituir o caractere \n de nova linha entre um caractere . de fullstop e qualquer caractere \S que não seja espaço em branco. Eu descobri como fazê-lo em regex101 aqui , mas queria saber se há um menor tr / sed / awk equivalente eu pode usar no meu shell bash. Algo como cat file.txt | ???

    
por weiji14 01.11.2018 / 21:49

3 respostas

3

perl tem um modo de parágrafo por meio da sinalização -00 perlrun , por isso, se substituirmos todas as novas linhas internas do seu input por um espaço:

$ wc -l input
       7 input
$ perl -00 -pe 's/\n(?!\Z)/ /g' input | wc -l
       3
$ 

O (?!\Z) bit não substitui as novas linhas no final de cada parágrafo, preservando assim os limites de parágrafo.

Outra opção é lex . Isso revela alguns pontos complicados, como manipular EOF e incluir sempre uma nova linha final (como exige o POSIX) e o que você define como um parágrafo: exatamente duas novas linhas ou qualquer número?

%%

[\n][\n]+ { printf("%s", yytext); }
\n        { int c = input();
            /* TODO book docs say this should return EOF on EOF ?? */
            if (c == 0) {
                putchar('\n');
                yyterminate();
            } else {
                printf(" %c", c);
            }
          }
<<EOF>>   { putchar('\n'); yyterminate(); }

%%

int main(int argc, char *argv[])
{
    return yylex();
}

Provavelmente precisa de mais testes do que

$ make paranlneg
lex  -o lex.paranlneg.c paranlneg.l
egcc -O2 -pipe    -o paranlneg lex.paranlneg.c  -ll
rm -f lex.paranlneg.c
$ perl -E 'say "a\nb\n\nc\nd"' | ./paranlneg
a b

c d
$ 
    
por 01.11.2018 / 21:58
2

Semelhante a @ resposta baseada em Perl do thrig mas usando o GNU Awk:

$ gawk -vRS= '{$1=$1; printf $0 RT}' file.txt
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Dictum sit amet justo donec enim diam vulputate. Nunc faucibus a pellentesque sit amet.

Quis enim lobortis scelerisque fermentum dui faucibus in. Leo duis ut diam quam nulla porttitor massa id neque. Vitae tortor condimentum lacinia quis vel eros.

Para uma solução rápida e sem precedentes, você pode usar o utilitário Coreutils fmt com um valor de largura adequado:

fmt -w1000 file.txt

(embora por padrão isso irá adicionar um espaço duplo após cada período).

    
por 01.11.2018 / 22:48
2

Abordagem baseada no GNU sed :

Você pode usar tr para substituir <newline> caracteres com <NUL> caracteres e, em seguida, usar sed para alterar sequências de dois ou mais caracteres <NUL> consecutivos em um caractere <newline> duplo e usar tr para substituir os caracteres <NUL> restantes por espaços em branco:

$ tr '\n' '
$ sed --null-data 's/\([^[:space:]]\)\n\([^[:space:]]\)/ /g' file.txt
' <file.txt | sed 's/\o000\{2,\}/\n\n/g' | tr '
$ tr '\n' '
$ sed --null-data 's/\([^[:space:]]\)\n\([^[:space:]]\)/ /g' file.txt
' <file.txt | sed 's/\o000\{2,\}/\n\n/g' | tr '%pre%' ' ' | sed --null-data 's/ $/\n/'
' ' ' | sed --null-data 's/ $/\n/'

Aqui, o último sed é necessário apenas para substituir o espaço restante final por uma nova linha.

Como alternativa (e mais concisa), você pode instruir sed a tratar seu arquivo como uma sequência de linhas terminadas em null (ou seja, sed considera uma única linha) e substituir por um único espaço em branco todas as ocorrências de uma única nova linha precedida e seguida por um caractere não espacial:

%pre%

Isso também preservará o espaçamento vertical entre parágrafos, ou seja, o número de novas linhas consecutivas. Eu preferi procurar um caractere não-espacial (em vez de um ponto) seguido por uma nova linha apenas para lidar com o caso de uma frase que não termina em um ponto final.

    
por 01.11.2018 / 23:06