Como posso corrigir linhas quebradas em lugares errados?

11

Meu arquivo de texto fica assim:

This is one
sentence that is broken.
However this is a good one.
And this
one is
somehow, broken into
many.

Desejo remover o caractere de nova linha à direita de qualquer linha que seja seguida por uma linha que comece com uma letra minúscula.

Portanto, isso deve ser:

This is one sentence that is broken.
However this is a good one.
And this one is somehow, broken into many.

Como posso fazer isso?

Edit: Existem algumas respostas muito boas aqui, mas eu escolhi aceitar a primeira que funcionou e foi a mais antiga. Muito obrigado a todos!


fonte
1
Látex? O problema é que você realmente não estabelece as regras para a quebra adequada de sentenças. Deseja colocar tudo, inclusive a pontuação no final da frase em uma única linha? Mas e se você tiver uma frase longa e ela sair da borda da janela de exibição?
Jamesqf
1
Eu me pergunto o que você realmente está tentando resolver? Talvez você deva usar a formatação de remarcação?
Curinga
@JeffSchaller Obrigado pelo lembrete! Eu tinha perdido de alguma forma. :)

Respostas:

7

experimentar

awk '$NF !~ /\.$/ { printf "%s ",$0 ; next ; } {print;}' file

Onde

  • $NF !~ /\.$/ linha de correspondência em que o último elemento não termina com um ponto,
  • { printf "%s ",$0 imprima esta linha com um espaço de destaque e sem alimentação de linha,
  • next ; } buscar a próxima linha,
  • {print;} e imprima.

Estou certo de que haverá uma sedopção.

Nota: isso funcionará com a linha que termina em um ponto; no entanto, a condição nas frases que começam com maiúsculas não será mesclada. Veja a resposta de Stéphane Chazelas.

Archemar
fonte
Se você gosta inteligente (muitos não o fazem)awk 'ORS=$NF~/\.$/?"\n":" "'
dave_thompson_085
10

Com awk:

awk -v ORS= '{print (NR == 1 ? "" : /^[[:lower:]]/ ? " " : RS) $0}
             END {if (NR) print RS}'

Ou seja, não anexe o separador de registros a cada linha (ORS vazio). Mas acrescente um separador de registros antes da linha atual, se não estiver na primeira linha, e a linha atual não começa com uma letra minúscula. Caso contrário, adicione um caractere de espaço, exceto na primeira linha.

Stéphane Chazelas
fonte
Quando eu executo isso, alguns pares de palavras são concatenados. Por exemplo, And thisone issomehow, broken intomany.eu não sei, awkmas as linhas devem ser unidas <space>além de RS? Ou esse erro é do usuário?
B Camada de
@ Blayer, bem localizado, obrigado. Deve ser corrigido agora.
Stéphane Chazelas
Sem problemas. Embora se pergunte de onde vieram os 11 votos positivos. Deve ser bom ter pessoas apenas assumindo que você está sempre certo. ;)
Camada B
4

Em perl:

#!/usr/bin/perl -w
use strict;
my $input = join("", <>);
$input =~ s/\n([a-z])/ $1/g;
print $input;

Tecnicamente, você desejava substituir "nova linha seguida por letra minúscula" por "espaço e essa letra minúscula", que é o que o núcleo do script perl acima faz:

  1. Leia na entrada uma string input.
  2. Atualize a inputvariável para ser o resultado da operação de pesquisa e substituição.
  3. Imprima o novo valor.
Jeff Schaller
fonte
1
um bom!! traduzido para um forro, perl -0777 -pe 's/\n([a-z])/ $1/g'e pode igualmente ser feito com o GNU sed como sed -zE 's/\n([a-z])/ \1/g'(assumindo de entrada não têm caracteres nulos)
Sundeep
3
@ Sundeep, ou perl -Mopen=locale -0777 -pe 's/\n(?=[[:lower:]])/ /g'para que não se limite a letras ASCII.
Stéphane Chazelas
4

Com sedvocê, você pode usar um N;P;Dciclo (para sempre ter duas linhas no espaço do padrão e, se o primeiro caractere após a nova linha estiver em minúsculas, substitua a nova linha por um espaço) e test - assim, após cada ssubstituição, você reiniciar o ciclo:

sed -e :t -e '$!N;/\n[[:lower:]]/s/\n/ /;tt' -e 'P;D' infile
don_crissti
fonte
1
Acho que vejo o que está acontecendo aqui, mas uma resposta expandida ajudaria aqueles que não usam loops de sed e espaços de padrão com muita frequência.
319 Joe
@ Joe - o que você quer dizer com "não usar o espaço do padrão com muita frequência" ? É aí que quase todas as operações ocorrem - o espaço de espera é um "espaço de armazenamento" - você não pode fazer nada com os dados enquanto estiverem lá. De qualquer forma, expliquei em detalhes como um N;P;Dciclo funciona aqui, para que eu não o revise novamente. A diferença aqui é a melhor t- verificar se algo foi substituído ou não - se o teste for bem-sucedido, ramificaremos para a parte superior do script, caso contrário, isso significa que nada foi substituído e P;Dé executado. Deixe-me saber se ainda não está claro.
31517 don_crissti
3

Usando sede fmt:

$ sed -e '1n; s/^[[:upper:]]/\n&/' input.txt | fmt
This is one sentence that is broken.

However this is a good one.

And this one is somehow, broken into many.

O script sed insere uma nova linha antes de cada linha que começa com uma letra maiúscula (exceto a primeira linha de entrada). sedA saída do é então canalizada fmtpara reformatar os parágrafos resultantes.

Como alternativa, use parse você o tiver instalado. É outro reformatador de parágrafos, mas muito mais capaz que fmt, com muito mais recursos e opções.

Observe que haverá uma linha em branco entre cada parágrafo. Os parágrafos devem ser separados um do outro por pelo menos uma linha em branco. Sem as linhas em branco, toda a amostra de entrada é reformatada como um único parágrafo com várias frases, por exemplo:

$ fmt input.txt
This is one sentence that is broken.  However this is a good one.
And this one is somehow, broken into many.

Se você precisar remover as linhas em branco após a reformatação, passe-a sednovamente - mas isso removerá TODAS as linhas em branco, incluindo as que estiverem na entrada original. por exemplo

$ sed -e '1n; s/^[[:upper:]]/\n&/' input.txt | fmt | sed -e '/^$/d'
This is one sentence that is broken.
However this is a good one.
And this one is somehow, broken into many.
cas
fonte
3

Outra maneira de fazer isso é:

perl -lpe '$\ = /\.$/ ? $/ : $"' data

em que: $\=> ORS, $/=> IRS= \n, $"=space

perl -pe '$_ .= <>, eof or redo if s/[^.]\K\n/ /' data

sed -e '
   :a
      /\.$/!N
      s/\n/ /
   ta
' data

fonte
2

Python 3

import re
print(re.sub(r'\n([a-z])', r' \1', open('file.txt').read(), flags=re.MULTILINE))

Esta é a mesma regra / substituição que a resposta de Jeff

wjandrea
fonte