Ich habe busybox 1.27.2
nur Zugriff auf.
Ich bearbeite gerade ein Wörterbuch mit über einer halben Million Wörtern und über 6.000 Seiten (welches mit Ghostscript aus einem PDF extrahiert und in Klartext umgewandelt wurde). Liegt in einer 20MB
.txt
Datei. Ursprünglich ist jedes einzelne Wort in diesem Wörterbuch mit einem Vorspann versehen, ->
um die Suche nach einem Wort zu erleichtern.
Ich versuche, es *nix
benutzerfreundlich zu gestalten. Das bedeutet, wenn ich Folgendes tue:
grep -e '->myfancyword' ./dictionary.txt
.
Als Ergebnis sollte ich erhalten:
->fancyword: This is a very fancy word. *Definition going on for more than 6 lines*
Das geht ganz einfach, indem man alle Zeilenumbrüche entfernt, \n
sodass jedes Wort seine gesamte Definition in einer sehr langen Zeile hat, was ok ist. Ich kann alles \n
durch ersetzen tr -d '\n'
und dann die Ausgabe davon durchlaufen lassen, sed 's/->/\n->/g'
sodass ich am Ende alle Wortdefinitionen in einer einzigen Zeile habe. Selbst bei diesem riesigen Dokument ist das in weniger als 5 Sekunden erledigt.
Ich erziele fast das gewünschte Ergebnis, aber es ist nicht perfekt. Ich kann dies tun grep -e '->word' ./dictionary.txt
und die vollständige Bedeutung des Wortes verstehen. Aber es ist kosmetisch nicht perfekt.
Der Grund, warum ich mit dem Ergebnis nicht zufrieden bin, liegt darin, dass das Original-PDF für den Ausdruck auf A4
mehreren Seiten formatiert war. Das bedeutet, dass lange Wörter abgeschnitten werden. So:
z.B
->word: This is a defini-
tion.
Wenn ich die Datei mit dem vorherigen Arbeitsablauf verarbeite, erhalte ich: ->word: This is a defini- tion.
beim Greppen das gewünschte Wort.
Bisher ist mir Folgendes gelungen:
- Eingang
->firstword: This is a defini-
tion.
->secondword: This is a second defini-
tion.
angewandt
tr -d '\n' < ./dictionary.txt > ./dictionary2.txt
Die Ausgabe ist:
->firstword: This is a defini- tion. ->secondword: This is a second defini- tion.
lief:
sed -e 's/->/\n->/g' ./dictionary2.txt
Abschließend:
Ausgabe
->firstword: This is a defini- tion.
->secondword: This is a second defini- tion.
Bevor ich den zweiten Schritt ausführe, möchte ich den Bindestrich und die neue Zeile ( -\n
) entfernen, um alle geschnittenen Zeilen miteinander zu „verbinden“.
Meine Frage lautet also: Wie kann ich die bestimmte Zeichenfolge ersetzen/löschen, die den Bindestrich -
und das Zeilenumbruchzeichen \n
( -\n
) am Ende der Zeile enthält?
Was ich gerne hätte, ist:
Ausgabe (überprüfen Sie bitte, dass der Bindestrich und das Leerzeichen ( -
) nicht mehr vorhanden sind)
->firstword: This is a definition.
->secondword: This is a second definition.
Danke schön.
BEARBEITEN:
Dies ist eine Seite der PDF-Datei:
->abigeato. (Del lat. abigeatus). 1. m. Am. Hurto de ganado.
->abigeo. (Del lat. abigeus). 1. m. Am. Ladrón de ganado.
->abigotado, da. 1. adj. bigotudo.
->abinar. 1. tr. rur. y vulg. Binar la tierra.
->abintestato. (De ab intestato). 1. m. Der. Procedimiento judicial sobre herencia y
adjudicación de bienes de quien muere sin testar.
->abiogénesis. (De a-2, bio- y -génesis). 1. f. Producción hipotética de seres vivos par-
tiendo de materia inerte. 2. f. Bioquím. síntesis abiótica.
->abiótico, ca. 1. adj. Biol. Se dice del medio en que no es posible la vida. V. síntesis
abiótica
->abipón, na. 1. adj. Se dice del individuo de un pueblo amerindio que habitaba cerca del
Paraná. U. t. c. s. 2. adj. Perteneciente o relativo a los abipones. 3. m. Lengua de la familia
guaicurú hablada por los abipones.
->abisagrar. 1. tr. Clavar o fijar bisagras en las puertas y sus marcos, o en otros objetos.
->abisal. (Del lat. abyssus). 1. adj. abismal (|| perteneciente al abismo). 2. adj. Se dice de
las zonas del mar profundo que se extienden más allá del talud continental, y corresponden a
profundidades mayores de 2000 m. 3. adj. Perteneciente o relativo a tales zonas.
->abiselar. 1. tr. biselar.
->abisinio, nia. 1. adj. Natural de Abisinia, hoy Etiopía. U. t. c. s. 2. adj. Perteneciente o re-
lativo a este país de África. 3. m. Lengua abisinia. V. rito abisinio
->abismado, da. (Del part. de abismar). 1. adj. Dicho de una persona, de su expresión, de
su gesto, etc.: Ensimismados, reconcentrados. 2. adj. Heráld. Dicho de una pieza del escudo:
Puesta en el abismo.
->abismal (1). (Del ár. hisp. almismár, y este del ár. clás. mismar). 1. m. Cada uno de los
clavos con que se fijaba en el asta el hierro de la lanza.abismal2. 1. adj. Perteneciente o re-
lativo al abismo. 2. adj. Muy profundo, insondable, incomprensible.
->abismar. 1. tr. Hundir en un abismo. U. t. c. prnl. 2. tr. Confundir, abatir. U. t. c. prnl. 3.
prnl. Entregarse del todo a la contemplación, al dolor, etc. 4. prnl. Am. sorprenderse (|| con-
moverse con algo imprevisto o raro).
->abismático, ca. 1. adj. abismal2.
->abismo. (Quizá del lat. vulg. *abyssimus, der. de abyssus, y este del gr. , sin fondo). 1.
m. Profundidad grande, imponente y peligrosa, como la de los mares, la de un tajo, la de una
sima, etc. U. t. en sent. fig. Se sumió en el abismo de la desesperación. 2. m. infierno (|| lugar
de castigo eterno). 3. m. Cosa inmensa, insondable o incomprensible. 4. m. Diferencia
grande entre cosas, personas, ideas, sentimientos, etc. 5. m. Heráld. Punto o parte central
del escudo. 6. m. Nic. Maldad, perdición, ruina moral.
Dies ist, was ich erhalte, wenn ich den normalen Text greppe, nachdem die Extraktion mit Ghostscript abgeschlossen ist (nur mit dos2unix verarbeitet):
grep -e '->abiog' ./rae-dos2unix.txt
->abiogénesis. (De a-2, bio- y -génesis). 1. f. Producción hipotética de seres vivos par-
Dies ist der Fall, wenn die vorherigen Schritte (1-4) für den Text ausgeführt wurden. Beim Greppen erhalte ich:
grep -e '->abiog' ./rae-una-linea.txt
->abiogénesis. (De a-2, bio- y -génesis). 1. f. Producción hipotética de seres vivos par- tiendo de materia inerte. 2. f. Bioquím. síntesis abiótica.
Antwort1
Dies ist in Perl ziemlich einfach. -0
Die Option von Perl weist das Programm an, als Trennzeichen für Eingabedatensätze NUL-Zeichen anstelle von Zeilenumbrüchen zu verwenden. Wenn die Eingabe also keine NUL-Zeichen enthält, wird die gesamte Eingabedatei als ein Datensatz behandelt. Selbst wenn NUL-Zeichen vorhanden sind, werden nachfolgende Datensätze genauso verarbeitet wie der erste.
Hinweis: Dies bedeutet, dass die gesamte Eingabedatei in den Speicher passen muss. Auf einem modernen System mit 16 GB oder mehr RAM ist dies wahrscheinlich kein Problem. Auf einem älteren System mit nicht genügend RAM, aber genügend Swap funktioniert es zwar immer noch, ist aber viel langsamer.
$ cat input.txt
->firstword: This is a defini-
tion.
->secondword: This is a second defini-
tion.
$ perl -0 -p -e 's/-\s*\n//g' input.txt
->firstword: This is a definition.
->secondword: This is a second definition.
Dadurch wird jede Folge von Bindestrichen gefolgt von null oder mehr Leerzeichen ( \s
, siehe unten), gefolgt von einem Zeilenumbruch ( \n
) entfernt.
Der \s*
Teil des regulären Ausdrucks ist dazu da, nachfolgende Leerzeichen abzugleichen, diekönnteam Ende einer Zeile stehen - meiner Erfahrung nach ist es sehr üblich, dass Textzeilen Leerzeichen am Ende haben (und sie sind schwer zu erkennen, da sie nicht druckbare Zeichen sind, also unsichtbar). Alternativ können Sie *
(null oder mehr) verwenden.RaumZeichen) oder [ \t]*
(null oder mehr Leerzeichen oder Tabulatoren) oder \h*
(null oder mehrhorizontalLeerzeichen) anstelle von \s*
.
Aus man perlre
:
Als Leerzeichen gelten die Zeichen, die Unicode als „Pattern White Space“ bezeichnet, und zwar:
U+0009 CHARACTER TABULATION U+000A LINE FEED U+000B LINE TABULATION U+000C FORM FEED U+000D CARRIAGE RETURN U+0020 SPACE U+0085 NEXT LINE U+200E LEFT-TO-RIGHT MARK U+200F RIGHT-TO-LEFT MARK U+2028 LINE SEPARATOR U+2029 PARAGRAPH SEPARATOR
ANMERKUNGEN:
- Ein
-
Zeichen ist nicht das einzige mögliche Bindestrich- oder Bindestrichzeichen, das verwendet werden kann. Wikipedia hat Seiten mit UnicodeBindestrichUndBindestrichZeichen. Glücklicherweise verfügt Perl über gute Unicode-Verarbeitungsfähigkeiten, sodass der Einzeiler so umgeschrieben werden kann, dass er\p{Dash}
(oder\p{Pd}
) anstelle von verwendet-
, um allen Zeichen der Bindestrichkategorie zu entsprechen:
$ perl -0 -p -e 's/\p{Dash}\h*\n//g' input.txt
->firstword: This is a definition.
->secondword: This is a second definition.
Dies behandelt jedoch Geviertstriche genauso wie Bindestriche (entfernt also einen Geviertstrich am Ende einer Zeile genauso wie einen Bindestrich) ... und es ist nicht ungewöhnlich, Geviertstriche anstelle von Klammern zu verwenden. Sie können stattdessen verwenden, \p{Hyphen}
wenn \p{Dash}
Sie nichts gegen eine Warnmeldung haben, dass „Bindestrich“ veraltet ist. Oder Sie können einen Klammerausdruck verwenden, der nur die Unicode-Codepunkte enthält, die Sie als Bindestriche behandeln möchten – z. B.
perl -0 -p -e 's/[\N{U+002D}\N{U+00AD}\N{U+2010}\N{U+2011}]\h*\n//g' input.txt
Ich empfehle, nicht jede Wortdefinition mit einem beginnen zu lassen
->
. Dadurch wird die Suche nach einem Wort mit grep unnötig umständlich - der Suchstring muss in Anführungszeichen gesetzt werden (wegen des>
, das die Shell zur Umleitung verwendet) und mit einem vorangestellt werden--
(wegen des-
, sonst behandelt grep Ihr Suchmuster so, als ob Sie es als Optionen gemeint hätten). Sie können beispielsweise nicht einfach Folgendes tun:grep ^firstword: dictionary.txt
Stattdessen müssten Sie Folgendes tun:
grep -- '^->firstword:' dictionary.txt
Für ein besseres Beispiel habe ich den Text aus Ihrem Bild extrahiert mitTesseract-OCRund führen Sie es durch eine Version des Perl-Einzeilers aus, die auch alle Zeilenumbrüche entfernt, auf die nicht folgt ->
:
$ cat input2.txt
->abigeato. (Del lat. abigeatus). 1. m. Am. Hurto de ganado.
->abigeo. (Del lat. abigeus). 1. m. Am. Ladrén de ganado.
->abigotado, da. 1. adj. bigotudo.
->abinar. 1. tr. rur. y vulg. Binar la tierra.
->abintestato. (De ab intestato). 1. m. Der. Procedimiento judicial sobre herencia y
adjudicacion de bienes de quien muere sin testar.
Eiiftiénesis. (De a-2, bio- y -génesis). 1. f. Produccién hipotética de seres vivos par-
tiendo de materia inerte. 2. f. Bioquim. sintesis abistica,
->abidtico, ca. 1. adj. Biol. Se dice del medio en que no es posible la vida. V. sintesis
abidtica
->abipon, na. 1. adj. Se dice del individuo de un pueblo amerindio que habitaba cerca del
Parana. U. t.c. s. 2. adj. Perteneciente o relativo a los abipones. 3. m. Lengua de la familia
guaicurt hablada por los abipones.
->abisagrar. 1. tr. Clavar o fijar bisagras en las puertas y sus marcos, 0 en otros objetos.
->abisal. (Del lat. abyssus). 1. adj. abismal (|| perteneciente al abismo). 2. adj. Se dice de
las zonas del mar profundo que se extienden mas alla del talud continental, y corresponden a
$ perl -0 -p -e 's/[\N{U+002D}\N{U+00AD}\N{U+2010}\N{U+2011}]\h*\n//g; s/\n+(?!->)//g' input2.txt
->abigeato. (Del lat. abigeatus). 1. m. Am. Hurto de ganado.
->abigeo. (Del lat. abigeus). 1. m. Am. Ladrén de ganado.
->abigotado, da. 1. adj. bigotudo.
->abinar. 1. tr. rur. y vulg. Binar la tierra.
->abintestato. (De ab intestato). 1. m. Der. Procedimiento judicial sobre herencia yadjudicacion de bienes de quien muere sin testar.Eiiftiénesis. (De a-2, bio- y -génesis). 1. f. Produccién hipotética de seres vivos partiendo de materia inerte. 2. f. Bioquim. sintesis abistica,
->abidtico, ca. 1. adj. Biol. Se dice del medio en que no es posible la vida. V. sintesisabidtica
->abipon, na. 1. adj. Se dice del individuo de un pueblo amerindio que habitaba cerca delParana. U. t.c. s. 2. adj. Perteneciente o relativo a los abipones. 3. m. Lengua de la familiaguaicurt hablada por los abipones.
->abisagrar. 1. tr. Clavar o fijar bisagras en las puertas y sus marcos, 0 en otros objetos.
->abisal. (Del lat. abyssus). 1. adj. abismal (|| perteneciente al abismo). 2. adj. Se dice delas zonas del mar profundo que se extienden mas alla del talud continental, y corresponden a
Ich empfehle dennoch, die ->
Sequenz aus der endgültigen Ausgabedatei zu entfernen. Sie ist bei der Textverarbeitung ein nützlicher Marker, später jedoch problematisch.
Ein Kommentar von @zevzek löst das Problem „verbraucht enorme Mengen RAM“. Verwenden Sie als ->
Trennzeichen statt NUL als Trennzeichen für Eingabedatensätze. Dadurch liest das Perl-Skript nur jeweils eine Wortdefinition und nicht die gesamte Datei auf einmal. Dadurch läuft es bei einer sehr großen Eingabedatei viel schneller, da nicht der gesamte verfügbare RAM verwendet wird und das System nicht ausgelagert werden muss.
Weitere Änderungen am Skript sind erforderlich, da wir nun die Zeichenfolge behandeln, die denAnfangeiner neuen Wortdefinition alsEndeder vorherigen Definition. Konkret müssen wir nun:
- Ändern Sie die Befehlszeilenoption
-p
(immer den aktuellen Datensatz ausgeben) in-n
(den aktuellen Datensatz nur auf Anweisung ausgeben). - Entfernen Sie die Zeilenendezeichen (
chomp()
dies erledigt die Funktion von Perl). - Überprüfen Sie, ob der Eingabedatensatz leer ist oder nur Leerzeichen enthält, da es dannimaginärleerer Datensatz vor dem ersten tatsächlichen Datensatz „abigeato“ und das wollen wir nicht ausdrucken. (Warum gibt es plötzlich einen imaginären leeren Datensatz? Weil
->
jetzt das Ende eines Datensatzes anzeigt, nicht den Anfang eines neuen. Das->
in->abigeato
ist das Trennzeichen zwischen dem vorherigen (leeren) Datensatz und dem neuen „abigeato“-Datensatz) - Drucken Sie den geänderten Datensatz mit "->" und einer neuen Zeile.
Zusammen würden diese den endgültigen Einzeiler wie folgt verändern:
$ perl -0 -p -e 's/[\N{U+002D}\N{U+00AD}\N{U+2010}\N{U+2011}]\h*\n//g;
s/\n+(?!->)//g' input2.txt
dazu:
perl -n -e 'BEGIN { $/="->" };
chomp;
next if m/^\s*$/;
s/[\N{U+002D}\N{U+00AD}\N{U+2010}\N{U+2011}]\h*\n//g;
s/\n+//g;
print "->$_\n"' input2.txt
Die Ausgabe dieser Version ist die gleiche wie die des Originals, außer dass die letzte Ausgabezeile garantiert mit einem Zeilenumbruch ( \n
) endet. Das Original garantierte dies nicht, sondern verhinderte es sogar, indem alle Zeilenumbrüche entfernt wurden, denen kein folgte ->
. Dies ist ein kostenloser Bonus, da eine Datei unter Unix technisch gesehen nur dann eine Textdatei ist, wenn jede einzelne Zeile mit \n
... endet. Meistens spielt dies keine Rolle (zumindest nicht bei modernen Versionen von Standard-Textverarbeitungstools), aber einige Programme verarbeiten die letzte Zeile einer „Textdatei“ nicht richtig, wenn sie nicht mit endet \n
.
(Übrigens könnte das Original behoben werden, indem ein END-Block hinzugefügt wird, um am Ende der Ausgabe eine neue Zeile einzufügen: END { print "\n" }
)
$/
ist eine Perl-Variable, die den Eingabedatensatz-Trennzeichen definiert ( man perlvar
Details zu den vordefinierten/speziellen/Steuervariablen von Perl finden Sie unter), ähnlich der RS
Variable in awk
. Zuvor habe ich die Perl- -0
Option verwendet, um es auf das NUL-Zeichen zu setzen ( man perlrun
Details zu den Befehlszeilenoptionen von Perl finden Sie unter ).
BEGIN
Anweisungen erfolgen einmal am Anfang eines Skripts, vor und außerhalb der impliziten Schleife, die durch die Verwendung der Optionen oder while(<>) { ..... }
von Perl verursacht wird (die Perl dazu bringen, sich wie ein Superheld oder zu verhalten ). Ebenso erfolgt eine Anweisung einmal am Ende eines Skripts, nachdem alle Eingaben gelesen und verarbeitet wurden.-p
-n
sed
sed -n
END
Antwort2
Ich schlage vor, dies in einem einzigen Skript nach dem folgenden Muster zu tun N;P;D
:
sed -e ':loop' -e '$!N;/\n->/!s/-*\n/ /;tloop' -e 'P;D'
Sie führen eine Schleife durch, indem Sie die nächste Zeile hinzufügen und die Zeilenumbrüche mit einem optionalen Bindestrich ( s/-*\n/ /
) entfernen, bis die neue Zeile mit beginnt ->
.