Ist es mit Gedit oder der Befehlszeile möglich, jede vierte Zeile einer Textdatei zu ändern?

11

Ich versuche, eine Textdatei in eine tabulatorgetrennte Tabelle zu konvertieren. Meine Textdatei sieht ungefähr so ​​aus:

Dog
Cat
Fish
Lizard
Wolf
Lion
Shark
Gecko
Coyote
Puma
Eel
Iguana

Mit den Standardfunktionen zum Suchen und Ersetzen in Gedit oder LibreOffice ist es einfach, das Zeilenende durch eine Registerkarte zu ersetzen. Aber wenn ich nur die Wagenrückläufe gegen Tabs tausche, bekomme ich Folgendes:

Dog   Cat   Fish   Lizard   Wolf   Lion   Shark   Gecko   Coyote   Puma   Eel   Iguana

Aber was ich tun muss, ist, dass es so aussieht:

Dog   Cat   Fish   Lizard
Wolf   Lion   Shark   Gecko  
Coyote   Puma   Eel   Iguana

Kann ich also jedes Zeilenendezeichen gegen eine Registerkarte mit Ausnahme jeder vierten Zeile austauschen?

Ich weiß nicht, ob diese Art der bedingten Iteration mit regulären Ausdrücken in einem Programm wie Gedit oder LibreOffice durchgeführt werden kann. Vielleicht muss dies also eine Art Befehlszeilenfunktion sein? Mir ist nicht einmal klar, mit welchem ​​Tool ich am besten anfangen soll.


Aktualisieren:

Ich habe die folgenden Befehle ausprobiert:

sed 'N;N;N;s/\n/\t/g' file > file.tsv

paste - - - - < file > file.tsv

pr -aT -s$'\t' -4 file > file.tsv

xargs -d '\n' -n4 < inputfile.txt

Aber wenn ich versuche, die resultierende tsvDatei in LibreOffice zu öffnen , sind die Spalten nicht ganz richtig. Ich bin nicht sicher, ob dies bedeutet, dass ich die oben genannten Befehle nicht korrekt ausführe oder ob ich in der LibreOffice-Importfunktion etwas falsch mache:

TSV-Eröffnung in Calc

Nur als Referenz sollte das gewünschte Ergebnis folgendermaßen aussehen:

Richtige Spalten

Fragesteller
quelle

Antworten:

16

Sie können einen Befehlszeileneditor wie zsed

sed 'N;N;N;s/\n/\t/g' file > file.tsv

oder, programmatisch, durch das Hinzufügen Backslash Zeilenfortsetzungszeichen zu jedem der Linien wollen Sie beitreten mit GNU sed n skip mAdreßoperator und im Anschluss mit dem klassischen Einzeiler für die weiteren Verbindungslinien:

sed '0~4! s/$/\t\\/' file | sed -e :a -e '/\\$/N; s/\\\n//; ta'

Siehe zum Beispiel Sed One-Liner Explained :

  1. Fügen Sie eine Zeile an die nächste an, wenn sie mit einem Backslash "\" endet.

    sed -e :a -e '/\\$/N; s/\\\n//; ta'
    

IMHO wäre es jedoch einfacher mit einem der anderen Standard-Textverarbeitungsprogramme, z

paste - - - - < file > file.tsv

(Die Anzahl der -Willen entspricht der Anzahl der Spalten) oder

pr -aT -s$'\t' -4 file > file.tsv

(Sie können das weglassen, -s$'\twenn Sie nichts dagegen haben, dass die Ausgabe durch mehrere Registerkarten getrennt wird.)


Das seltsame Verhalten beim erneuten Importieren, das Sie beobachten, ist mit ziemlicher Sicherheit darauf zurückzuführen, dass die Originaldatei CRLF-Zeilenenden im Windows-Stil aufweist. Wenn Sie mit Dateien von Windows arbeiten müssen, können Sie die Konvertierung auf verschiedene Arten in den Befehl rollen, z

tr -d '\r' < file.csv | paste - - - -

oder

sed 'N;N;N;s/\r\n/\t/g' file.csv

Ersteres entfernt ALLE Wagenrückläufe, während letzteres eine CR am Ende jeder der neuen Zeilen beibehält (was möglicherweise gewünscht wird, wenn sich der beabsichtigte Endbenutzer unter Windows befindet).

Steeldriver
quelle
1
Ein Hinweis zu Zeilenenden im Windows-Stil: Die Standardwerkzeuge zum Konvertieren zwischen ihnen und dem Unix-Stil sind dos2unixund unix2dos.
David Foerster
13

Sie können xargsimmer vier Zeilen zu einer gruppieren, die jeweils durch ein Leerzeichen getrennt sind:

xargs -d '\n' -n4 < inputfile.txt

-d '\n'Setzt das Eingabetrennzeichen auf ein Zeilenumbruchzeichen, da es sonst auch bei Leerzeichen unterbrochen wird. Wenn Sie ohnehin nur ein Wort pro Eingabezeile haben, können Sie dies sogar weglassen.
-n4Setzt die Argumentnummer (die Anzahl der Eingabeelemente pro Ausgabezeile) auf 4.

Ausgabe:

Dog Cat Fish Lizard
Wolf Lion Shark Gecko
Coyote Puma Eel Iguana

Wenn Sie Registerkarten als Trennzeichen anstelle eines Leerzeichens verwenden möchten, können Sie diese anschließend ersetzen. Wenn Sie jedoch Leerzeichen in Ihren Eingabezeilen hätten, würden diese ebenfalls ersetzt:

xargs -d '\n' -n4 | tr ' ' '\t'

Ausgabe (abhängig von der Registerkartenbreite des Browsers / Terminals):

Dog Cat Fish    Lizard
Wolf    Lion    Shark   Gecko
Coyote  Puma    Eel Iguana
Byte Commander
quelle
Diese Methode hat den Vorteil, dass sie sich auch dann angemessen verhält, wenn die Gesamtzahl der Eingabezeilen kein Vielfaches von vier ist.
Eliah Kagan
3

Sie könnten auch verwenden:

awk -v ORS="" '{print $1; print NR%4==0?"\n":"\t"}' file > file.tsv 

Die zwei integrierten awk-Variablen sind:

  • ORS: O utput R ECORD S eparator (default = Neue - Zeile). Es wird am Ende jedes Druckbefehls hinzugefügt.
  • NR: N umber des aktuellen R ow awk verarbeitet.

Dieser Befehl zeigt für jede Zeile den Inhalt der ersten (und nur hier) Spalte an. Anschließend wird eine neue Zeile oder eine Registerkarte hinzugefügt, indem der Rest der Division NRvon 4 getestet wird.

Arauk
quelle
3

Ein weiterer kürzester awkAnsatz:

awk '{printf $0 (NR%4?"\t":"\n")}' infile

Diese printf die nur eine Spalte , gefolgt von nächsten und übernächsten und ... und ein Tab \tZeichen nach jeder aber wird printf einen \newline Charakter , wenn N Umbra von R ecord Faktor war 4 (wo NR%4zurückkehren 0 (false) , das , was Ternary Operator ist condition(s)?when-true:when-falsemacht gerade.)

αғsнιη
quelle
3

Meine Lösung hierfür wäre die Verwendung einer Kombination aus sedund sed. Erstens könnten Sie jede vierte Zeile mit einem Sonderzeichen markieren, beispielsweise >mit dieser Lösung:

In diesem Fall möchten Sie mit Zeile 5 beginnen und jede vierte Zeile danach markieren. In GNU sedkann dies als Adresse angegeben werden 5~4. Sie können diesen Befehl verwenden:

sed '5~4s/^/>/' file1 > file2

Dann müssen Sie die Zeilenumbrüche entfernen, was mit einer sedSchleife erfolgen kann:

sed ':a;N;s/\n/ /;ba' file2 > file3

Es gibt einfachere Möglichkeiten, Zeilenumbrüche in andere Zeichen umzuwandeln, z. B tr.:

tr '\n' ' ' < file2 > file3

In beiden Fällen ergibt die Kombination der beiden

Dog   Cat   Fish   Lizard   >Wolf   Lion   Shark   Gecko   >Coyote   Puma   Eel   Iguana

(Die sedVersion hinterlässt einen nachgestellten Zeilenumbruch, die trVersion jedoch nicht.)

Danach müssen Sie nur noch die von Ihnen eingefügten Sonderzeichen in Zeilenumbrüche konvertieren. Siehe zum Beispiel Konvertieren einer durch Tabulatoren getrennten Datei, um Zeilenumbrüche zu verwenden . Wechseln Sie >in diesem Fall zu Zeilenumbrüchen:

sed 'y/>/\n/' file3 > outfile

Der yBefehl hat dieselbe Funktion wie das trTransformieren eines Zeichens in ein anderes, aber Sie können den sBefehl hier genauso gut verwenden. Mit smüssen Sie gjedes Match in der Zeile ( sed 's/>/\n/g') bearbeiten.

Anstatt zwei Zwischendateien zu erstellen, können Sie Pipes verwenden:

$ sed '5~4s/^/>/' file | sed ':a;N;s/\n/ /;ba' | sed 'y/>/\n/'
Dog Cat Fish Lizard 
Wolf Lion Shark Gecko 
Coyote Puma Eel Iguana

Wenn nachgestellte Leerzeichen ein Problem darstellen, können Sie einen weiteren Befehl hinzufügen, um sie zu entfernen:

| sed 's/ $//'
spaceman117X
quelle
2

Der "Vollständigkeit halber" ist hier eine reine Bash-Lösung:

#!/usr/bin/env bash

sep=$'\t'

while read one \
      && read two \
      && read three \
      && read four
do
  printf "%s\n" "$one$sep$two$sep$three$sep$four"
done

Funktioniert auch mit Leerzeichen, vorausgesetzt, es IFSist richtig eingestellt (was standardmäßig AFAIK sein sollte). Darüber hinaus denke ich, dass dies sogar ein portables Shell-Skript sein und mit jeder POSIX-kompatiblen Shell funktionieren könnte.

Daniel Jour
quelle
1
Dies ist im Allgemeinen nicht auf POSIX-kompatible Shells portierbar, da die $' 'Form des Zitierens von POSIX nicht benötigt wird. Beispiel: In dash(das shstandardmäßig unter Ubuntu bereitgestellt wird) werden printf '%s\n' $'a\tb'nur Ausgaben ausgeführt $a\tb. Das heißt aber nicht, dass dies nicht nützlich ist. es funktioniert in bash. Wie bei einigen anderen Lösungen, die veröffentlicht wurden, wird jedoch eine unvollständige Ausgabe erstellt, wenn die Anzahl der Eingabezeilen nicht ein Vielfaches von vier ist. Außerdem empfehle ich die Verwendung read -r, da hier kein Grund zu der Annahme besteht, dass die Erweiterung von Backslash-Escapezeichen in der Eingabedatei erwünscht ist.
Eliah Kagan
Sie könnten einfach tunprintf '%s\t%s\t%s\t%s\n' "$one" "$two" "$three" "$four"
terdon
2

Ein vim-Makro (aufgezeichnet mit q) kann Ihre Operation anwenden und dann drei Zeilen überspringen. Dann führen Sie dieses Makro nur n Mal aus.

z.B:

qq $ J i <TAB> <ESC> $ J i <TAB> <ESC> $ J i <TAB> <ESC> ^^ j qq 100 @q
Rackandboneman
quelle
2

Da Sie nach einer Gedit-Lösung gefragt haben, sollte so etwas funktionieren:

Finden:

(\w+)[\r\n]+(\w+)[\r\n]+(\w+)[\r\n]+(\w+)[\r\n]+

Ersetzen mit:

\1\t\2\t\3\t\4\n

Stellen Sie sicher, dass das Kontrollkästchen für reguläre Ausdrücke aktiviert ist.

Wie es funktioniert:

Der erste Schritt besteht darin, eine Reihe von Wortzeichen mit \ w + zu finden und die Ergebnisse in der Variablen \ 1 zu erfassen, indem der Ausdruck in Klammern gesetzt wird:

(\w+)

Als nächstes suchen wir nach einer Reihe von Zeilenendezeichen \ r und \ n oder CR und LF. Da Windows-formatierte Dateien beide verwenden, erstellen wir eine Zeichenklasse, indem wir diese beiden Zeichen in eckige Klammern setzen. Mit dem Plus wird nach einem oder mehreren Zeichen gesucht:

[\r\n]+

Schließlich wiederholen wir dies noch dreimal und speichern jedes nachfolgende Wort in den Variablen \ 2, \ 3 und \ 4. Dies macht unser Ersetzen durch Ausdruck einfach. Wir müssen nur die Tabulatorzeichen \ t und ein neues Zeilenzeichen \ n an den entsprechenden Stellen für die gewünschte Formatierung platzieren.

Jason Wood
quelle