¿Cómo puedo arreglar líneas rotas en lugares equivocados?

11

Mi archivo de texto se ve así:

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

Quiero eliminar el carácter de nueva línea al final de cualquier línea seguida de una línea que comience con una letra minúscula.

Entonces esto debería ser:

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

¿Cómo puedo hacer esto?

Editar: Hay algunas respuestas realmente buenas aquí, pero elegí aceptar la primera que funcionó y fue la más temprana. Muchas gracias a todos!


fuente
1
¿Látex? El problema es que realmente no declaras las reglas para romper las oraciones correctamente. ¿Desea poner todo hasta la puntuación de final de frase en una sola línea? Pero, ¿qué pasa si tiene una oración larga y se sale del borde de su ventana de visualización?
jamesqf
1
Me pregunto qué es lo que realmente estás tratando de resolver. ¿Quizás debería usar el formato de rebajas?
Comodín el
@JeffSchaller ¡Gracias por el recordatorio! Me había perdido de alguna manera. :)

Respuestas:

7

tratar

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

dónde

  • $NF !~ /\.$/ línea de coincidencia donde el último elemento no termina con un punto,
  • { printf "%s ",$0 imprima esta línea con un espacio de seguimiento y sin avance de línea,
  • next ; } buscar la siguiente línea,
  • {print;} e imprimirlo.

Estoy seguro de que habrá una sedopción.

Nota: esto funcionará con una línea que termina en un punto, sin embargo, la condición en las oraciones que comienzan con mayúscula no se fusionará. Ver la respuesta de Stéphane Chazelas.

Archemar
fuente
Si te gusta inteligente (muchos no)awk 'ORS=$NF~/\.$/?"\n":" "'
dave_thompson_085
10

Con awk:

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

Es decir, no agregue el separador de registros a cada línea (ORS vacío). Pero anteponga un separador de registro antes de la línea actual si no está en la primera línea y la línea actual no comienza con una letra minúscula. De lo contrario, anteponga un carácter de espacio, excepto en la primera línea.

Stéphane Chazelas
fuente
Cuando ejecuto esto, algunos pares de palabras se concatenan. Por ejemplo And thisone issomehow, broken intomany., no sé, awkpero ¿deberían unirse las líneas <space>además de RS? ¿O es este error del usuario?
Capa B
@BLayer, bien visto, gracias. Debería arreglarse ahora.
Stéphane Chazelas
No hay problema. Aunque uno se pregunta de dónde vinieron los 11 votos a favor. Debe ser agradable que la gente asuma que siempre tienes razón. ;)
Capa B
4

En perl:

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

Técnicamente, quería reemplazar "nueva línea seguida de letra minúscula" por "espacio y esa letra minúscula", que es lo que hace el núcleo del script perl anterior:

  1. Leer en la entrada a una cadena input.
  2. Actualice la inputvariable para que sea el resultado de la operación de búsqueda y reemplazo.
  3. Imprime el nuevo valor.
Jeff Schaller
fuente
1
bueno !! traducido a one-liner, perl -0777 -pe 's/\n([a-z])/ $1/g'y de manera similar se puede hacer con GNU sed como sed -zE 's/\n([a-z])/ \1/g'(suponiendo que la entrada no tenga caracteres nulos)
Sundeep
3
@Sundeep, o perl -Mopen=locale -0777 -pe 's/\n(?=[[:lower:]])/ /g'para que no se limite a letras ASCII.
Stéphane Chazelas
4

Con sedpodría usar un N;P;Dciclo (para tener siempre dos líneas en el espacio del patrón y si el primer carácter después de la nueva línea está en minúscula, reemplace la nueva línea con un espacio) y un test - de esa manera después de cada ssustitución reinicie el ciclo:

sed -e :t -e '$!N;/\n[[:lower:]]/s/\n/ /;tt' -e 'P;D' infile
don_crissti
fuente
1
Creo que veo lo que está sucediendo aquí, pero una respuesta ampliada nos ayudaría a aquellos de nosotros que no usamos bucles de sed y espacios de patrones muy a menudo.
Joe
@ Joe: ¿qué quieres decir con "no usar el espacio de patrón muy a menudo" ? Ahí es donde tienen lugar casi todas las operaciones: el espacio de retención es un "espacio de almacenamiento": no puede hacer nada con los datos mientras está allí. De todos modos, he explicado en detalle cómo funciona un N;P;Dciclo aquí para que no lo repita nuevamente. La diferencia aquí es la mejor t, para verificar si algo fue reemplazado o no, si la prueba es exitosa, entonces saltamos a la parte superior del script, de lo contrario significa que nada fue reemplazado y P;Dse ejecuta. Avísame si aún no está claro.
don_crissti
3

Usando sedy 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.

La secuencia de comandos sed inserta una nueva línea antes de cada línea que comienza con una letra mayúscula (excepto la primera línea de entrada). sedLa salida de la salida se canaliza fmtpara formatear los párrafos resultantes.

Alternativamente, use parsi lo tiene instalado. Es otro formateador de párrafos, pero mucho más capaz que fmt, con muchas más características y opciones.

Tenga en cuenta que habrá una línea en blanco entre cada párrafo. Los párrafos deben estar separados entre sí por al menos una línea en blanco. Sin las líneas en blanco, toda su muestra de entrada se reformatea como un solo párrafo de varias oraciones, por ejemplo:

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

Si necesita eliminar las líneas en blanco después de sedvolver a formatear, simplemente vuelva a conectarlo, pero esto eliminará TODAS las líneas en blanco, incluidas las que puedan haber estado en la entrada original. p.ej

$ 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
fuente
3

Otra forma de hacer esto es:

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

en donde: $\=> ORS, $/=> IRS= \n, $"=space

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

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

fuente
2

Python 3

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

Esta es la misma expresión regular / sustitución que la respuesta de Jeff

wjandrea
fuente