Wie kann ich an falschen Stellen unterbrochene Linien reparieren?

11

Meine Textdatei sieht folgendermaßen aus:

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

Ich möchte das nachfolgende Zeilenumbruchzeichen für jede Zeile entfernen, auf die eine Zeile folgt, die mit einem Kleinbuchstaben beginnt.

Das sollte also sein:

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

Wie kann ich das machen?

Bearbeiten: Hier gibt es einige wirklich gute Antworten, aber ich habe mich dafür entschieden, die erste zu akzeptieren, die funktioniert hat und am frühesten war. Vielen Dank an alle!


quelle
1
Latex? Das Problem ist, dass Sie die Regeln für das richtige Brechen von Sätzen nicht wirklich angeben. Möchten Sie alles bis einschließlich der Satzzeichen-Interpunktion in einer einzigen Zeile zusammenfassen? Aber was ist, wenn Sie einen langen Satz haben und dieser am Rand Ihres Anzeigefensters abläuft?
Jamesqf
1
Ich frage mich, was Sie wirklich zu lösen versuchen? Vielleicht sollten Sie die Markdown-Formatierung verwenden?
Wildcard
@ JeffSchaller Danke für die Erinnerung! Ich hatte es irgendwie verpasst. :)

Antworten:

7

Versuchen

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

wo

  • $NF !~ /\.$/ Übereinstimmungslinie, bei der das letzte Element nicht mit einem Punkt endet,
  • { printf "%s ",$0 Drucken Sie diese Zeile mit einem Leerzeichen und ohne Zeilenvorschub.
  • next ; } nächste Zeile holen,
  • {print;} und drucken Sie es aus.

Ich bin sicher, dass es eine sedOption geben wird.

Hinweis: Dies funktioniert mit Zeilen, die mit einem Punkt enden. Die Bedingung in Sätzen, die mit Großbuchstaben beginnen, wird jedoch nicht zusammengeführt. Siehe die Antwort von Stéphane Chazelas.

Archemar
quelle
Wenn Sie klug mögen (viele nicht)awk 'ORS=$NF~/\.$/?"\n":" "'
dave_thompson_085
10

Mit awk:

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

Fügen Sie das Datensatztrennzeichen nicht an jede Zeile an (ORS leer). Aber prepend einen Rekord Separator vor der aktuellen Zeile , wenn nicht in der ersten Zeile und der aktuellen Zeile nicht mit einem Kleinbuchstaben beginnen. Andernfalls stellen Sie stattdessen ein Leerzeichen voran, außer in der ersten Zeile.

Stéphane Chazelas
quelle
Wenn ich dies ausführe, werden einige Wortpaare verkettet. Zum Beispiel And thisone issomehow, broken intomany.weiß ich nicht, awkaber sollten Linien <space>zusätzlich zu verbunden werden RS? Oder ist das ein Benutzerfehler?
B-Schicht
@ BLayer, gut gesehen, danke. Sollte jetzt behoben sein.
Stéphane Chazelas
Kein Problem. Obwohl man sich fragt, woher die 11 positiven Stimmen kamen. Es muss schön sein, wenn die Leute einfach davon ausgehen, dass Sie immer Recht haben. ;)
B Schicht
4

In Perl:

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

Technisch gesehen wollten Sie "Zeilenumbruch gefolgt von Kleinbuchstaben" durch "Leerzeichen und Kleinbuchstaben" ersetzen. Dies ist der Kern des obigen Perl-Skripts:

  1. Lesen Sie die Eingabe in eine Zeichenfolge ein input.
  2. Aktualisieren Sie die inputVariable als Ergebnis des Such- und Ersetzungsvorgangs.
  3. Drucken Sie den neuen Wert.
Jeff Schaller
quelle
1
gut !! übersetzt in einzeilig perl -0777 -pe 's/\n([a-z])/ $1/g'und kann in ähnlicher Weise mit GNU sed als sed -zE 's/\n([a-z])/ \1/g'(vorausgesetzt, die Eingabe hat keine Nullzeichen) durchgeführt werden
Sundeep
3
@Sundeep, oder perl -Mopen=locale -0777 -pe 's/\n(?=[[:lower:]])/ /g'damit es nicht auf ASCII-Buchstaben beschränkt ist.
Stéphane Chazelas
4

Mit können sedSie einen N;P;DZyklus verwenden (um immer zwei Zeilen im Musterbereich zu haben, und wenn das erste Zeichen nach der neuen Zeile in Kleinbuchstaben geschrieben ist, ersetzen Sie die neue Zeile durch ein Leerzeichen) und eine test - auf diese Weise sstarten Sie den Zyklus nach jeder Ersetzung neu:

sed -e :t -e '$!N;/\n[[:lower:]]/s/\n/ /;tt' -e 'P;D' infile
don_crissti
quelle
1
Ich glaube, ich sehe, was hier vor sich geht, aber eine erweiterte Antwort würde denjenigen von uns helfen, die Sed-Loops und Pattern-Spaces nicht sehr oft verwenden.
Joe
@ Joe - was meinst du mit "den Musterraum nicht sehr oft nutzen" ? Hier finden fast alle Vorgänge statt - der Speicherplatz ist ein "Speicherplatz" - Sie können mit den Daten nichts anfangen, während sie dort sind. Wie auch immer, ich habe hier ausführlich erklärt, wie ein N;P;DZyklus funktioniert, damit ich nicht noch einmal darauf eingehen werde. Der Unterschied besteht darin, dass t- um zu überprüfen, ob etwas ersetzt wurde oder nicht - wenn der Test erfolgreich ist, wir zum Anfang des Skripts verzweigen, andernfalls bedeutet dies, dass nichts ersetzt wurde und P;Dausgeführt wird. Lassen Sie mich wissen, wenn es noch unklar ist.
don_crissti
3

Verwenden von sedund 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.

Das sed-Skript fügt vor jeder Zeile, die mit einem Großbuchstaben beginnt, eine neue Zeile ein (mit Ausnahme der allerersten Eingabezeile). sedDie Ausgabe wird dann weitergeleitet, fmtum die resultierenden Absätze neu zu formatieren.

Alternativ verwenden, parwenn Sie es installiert haben. Es ist ein weiterer Absatzumformatierer, aber viel leistungsfähiger als fmtmit viel mehr Funktionen und Optionen.

Beachten Sie, dass zwischen jedem Absatz eine Leerzeile steht. Absätze sollten durch mindestens eine Leerzeile voneinander getrennt sein. Ohne die Leerzeilen wird Ihr gesamtes Eingabebeispiel als einzelner Absatz mit mehreren Sätzen neu formatiert, z.

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

Wenn Sie die Leerzeilen nach der Neuformatierung entfernen müssen, leiten Sie sie einfach sederneut durch. Dadurch werden jedoch ALLE Leerzeilen entfernt, einschließlich aller Zeilen, die möglicherweise in der ursprünglichen Eingabe enthalten waren. z.B

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

Eine andere Möglichkeit, dies zu tun, ist:

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

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

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

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

quelle
2

Python 3

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

Dies ist die gleiche Regex / Substitution wie Jeffs Antwort

wjandrea
quelle