Wie kann ich Text basierend auf seiner Einrückung markieren?

410
Ramaprakasha

Ich möchte Textinhalte (mit XML-ähnlichen Tags) basierend auf ihrer Eindringtiefe kennzeichnen. Leerzeilen sollten erhalten bleiben. Das folgende Beispiel hat einen mit 1, 2 oder 3 Registerkarten eingerückten Inhalt.

EINGANG

aaa bbb bbb  aaa  ccc ccc bbb bbb 

Ich möchte mit der gleichen Einzugsebene zu Gruppenleitungen und diese Einrückungen um Schlagworte übersetzen x, yund z, wie folgt aus :

AUSGABE

<x>aaa</x> <y>bbb bbb</y>  <x>aaa</x>  <z>ccc ccc</z> <y>bbb bbb</y> 

Wie kann ich das machen?

-1
Wenn Sie möchten, dass leere Zeilen hinzugefügt werden (wie Sie gezeigt haben), sollten Sie dies sagen. Und dein Beispiel ist inkonsistent - vergleiche die beiden Strophen. Scott vor 7 Jahren 0
Ich habe die Frage aufgrund Ihrer Meinung bearbeitet. Ramaprakasha vor 7 Jahren 0
Vielen Dank, dass Sie die inkonsistente Einrückung der Tags gegenüber den Daten korrigiert haben, aber Sie haben meinen Standpunkt bezüglich der Leerzeilen verfehlt. Ihr Eingabe-Dataset besteht aus zehn Zeilen: Zeile 1 ist "111", Zeile 2 und 3 sind beide "222", Zeile 4 ist leer usw. Die Ausgabe ist zwölf Zeilen lang. Dabei werden nicht nur die bereits vorhandenen Leerzeilen beibehalten, sondern hinzugefügt eine bei 1½ (zwischen “222” und “111”) und eine andere bei 8½ (zwischen “333” und “222”). Wollen Sie, dass das passiert? (Oder ist es ein Tippfehler in Ihrer Frage?) Scott vor 7 Jahren 0
Oh, Entschuldigung. Ja, es war ein Tippfehler. Ich habe es jetzt korrigiert. Ramaprakasha vor 7 Jahren 0
Da ich Text markieren wollte, änderte ich die Ziffern in "a", "b", "c". Ramaprakasha vor 7 Jahren 0
Sie möchten also alle `<>` Zeichen und alle Zeichen dazwischen entfernen, wobei Zeilenvorschübe und Wagenrücklauf sowie alle anderen Zeichen und Zeilenpositionen ohne die Zeichen, die entfernt werden, nur die Zeilenpositionen betreffen, entfernt werden. Dies hilft Ihnen zu klären, was Sie wollten Meiner Ansicht nach. Wahrscheinlich eine einfache Regex mit sed oder grep oder etwas für einen guten Ausgangspunkt. Pimp Juice IT vor 7 Jahren 0
Nein, es ist das Gegenteil davon. Ich habe jetzt durch Hinzufügen von Eingabe- und Ausgabeköpfen geklärt. Ramaprakasha vor 7 Jahren 0
Sie können vielleicht ein Skript schreiben, aber es wird ziemlich komplex. Sie müssen sich daran erinnern, an welchem ​​Tag Sie zuletzt geöffnet waren, und die Datei Zeile für Zeile lesen. Seth vor 7 Jahren 0
Hast du meine Antwort auf deine Frage gesehen? Funktioniert es für Sie? (Siehe [Was muss ich tun, wenn jemand meine Frage beantwortet?] (// superuser.com/help/someone-answers)) Scott vor 6 Jahren 0
Entschuldigung, ich hatte einen Absturz und musste mein Betriebssystem neu installieren, daher konnte ich Sie nicht beantworten. Ich habe deine Antwort akzeptiert. Es funktioniert perfekt Ramaprakasha vor 6 Jahren 0

1 Antwort auf die Frage

0
Scott

Problemstellung:

Eine Eingabedatei enthält Text, der um null oder mehr Tabulatorzeichen eingerückt ist. Im Einzelnen ist jede Zeile in der Eingabe eine der folgenden:

  • Leerzeichen oder
  • Null oder mehr Tabulatoren (bis zu einem Grenzwert; siehe unten), gefolgt von einem Zeichen, das weder ein Leerzeichen noch ein Tabulator ist (gefolgt von null oder mehr Zeichen).

Es gibt keine Zeilen dafür

  • Beginnen Sie mit null oder mehr Tabulatoren, gefolgt von einem Leerzeichen. (Dies bedeutet, dass es keine Zeilen gibt, die mit einem Leerzeichen beginnen.)
    Oder
  • Besteht vollständig aus einer oder mehreren Registerkarten (und nichts anderem).
    oder
  • Beginnen Sie mit mehr als einer angegebenen Anzahl von Registerkarten.

Die Eingabe wird logisch in Gruppen von Zeilen zerlegt, die entweder alle sind

  • Leerzeichen oder
  • Mit der gleichen Anzahl von Registerkarten eingerückt.

Leerzeilen werden unverändert an die Ausgabe übergeben.

Eine Liste von Tags muss angegeben werden. zB x, yund zz. Eine Gruppe von (nicht leeren) Zeilen, die mit Nullen (dh nicht eingerückt) eingerückt sind, wird durch <x>und eingeschlossen </x>. Eine Gruppe von Zeilen, die mit einem Tabulator eingerückt sind, wird durch <y>und eingeschlossen </y>. Eine Gruppe von Zeilen, die mit zwei Tabulatoren eingerückt sind, wird durch <zz>und eingeschlossen </zz>. (Zeilen werden nicht mit mehr als zwei Registerkarten eingerückt.)

In der ersten Zeile einer Gruppe (von nicht leeren Zeilen) wird das Anfangs-Tag zwischen den Registerkarten und dem Text eingefügt. In der letzten Zeile einer Gruppe wird das Ende-Tag am Ende des Textes angehängt. Eine Gruppe kann aus einer einzelnen Zeile bestehen, daher kann die erste Zeile auch die letzte Zeile sein. Alle Zeilen einer anderen Gruppe als der ersten werden zusätzlich um die Breite des Anfangs-Tags eingerückt (mit Leerzeichen zwischen den Registerkarten und dem Text).

Zum Beispiel ( ―→ um eine Registerkarte darzustellen), kann dieser INPUT :

aaa ―→ Once upon a midnight dreary, ―→ while I pondered, weak and weary,  Quoth the Raven, “Nevermore.”  ―→ ―→ The quick brown fox ―→ ―→ jumps over the lazy dog. ―→ It was a dark and stormy night. ―→ Suddenly a shot rang out. 

soll in dieses OUTPUT übersetzt werden :

<x>aaa</x> ―→ <y>Once upon a midnight dreary, ―→ while I pondered, weak and weary,</y>  <x>Quoth the Raven, “Nevermore.”</x>  ―→ ―→ <zz>The quick brown fox ―→ ―→ jumps over the lazy dog.</zz> ―→ <y>It was a dark and stormy night. ―→ Suddenly a shot rang out.</y> 

Lösung:

Natürlich wissen wir nicht so recht, was wir mit einer Eingabezeile tun sollen, bis wir die nächste Zeile gelesen haben. Dieses Problem wird normalerweise behoben, indem der Inhalt einer Zeile gespeichert wird, um verarbeitet zu werden, nachdem die nächste Zeile gelesen wurde.

Hier ist es also:

awk ' BEGIN { num_tags = split("x y zz", tags) for (i=1; i<=num_tags; i++) { len = length(tags[i]) + 2 tag_pad[i] = "" for (j=1; j<=len; j++) tag_pad[i] = tag_pad[i] " " } } { if (NF == 0) indent_num = 0 else { indent_num = index($0, $1) indent_str = substr($0, 1, indent_num-1) restOfLine = substr($0, indent_num) } if (indent_num != saved_indent_num && saved != "") { print saved "</" tags[saved_indent_num] ">" saved = "" } if (NF == 0) print else if (indent_num > num_tags) { errmsg = "Error: line %d has an indent level of %d.\n" printf errmsg, NR, indent_num > "/dev/stderr" exit 1 } else if (indent_num == saved_indent_num) { print saved saved = indent_str tag_pad[indent_num] restOfLine } else saved = indent_str "<" tags[indent_num] ">" restOfLine saved_indent_num = indent_num } END { if (saved != "") print saved "</" tags[saved_indent_num] ">" } ' 

Der BEGIN-Block initialisiert die Tags ( x, yund zz) durch Aufteilen einer durch Leerzeichen getrennten Zeichenfolge. Das tag_padArray enthält genügend Leerzeichen, um der Breite der Tags (einschließlich der <und >) zu entsprechen: tag_pad[1]und tag_pad[2]sind drei Leerzeichen. tag_pad[3]ist vier Räume.

Nachdem wir eine Zeile gelesen haben, analysieren wir sie. Wenn es keine Felder ( NF == 0) hat, muss es leer sein (da wir angegeben haben, dass keine Zeile ausschließlich aus Leerzeichen und Tabulatoren besteht), also indent_numauf 0 gesetzt. Andernfalls messen Sie den Einzug, indem Sie die Position von $1(das erste Wort) in $0( die gesamte Zeile).  indexgibt einen Wert zurück, der bei 1 beginnt, also um einen Wert größer als die Anzahl der Leerzeichen vor dem ersten Nicht-Leerzeichen (und denken Sie daran, dass dies alles Tabulatoren sind). Das ist ein Glück, denn jetzt indent_num entsprechen Einträge in den Arrays tagsund tag_pad. Dann zerlegen wir die Linie in einen indent_str(den Leerraum) und restOfLine(alles nach dem Einzug).

Jetzt verlassen wir uns auf gespeicherte Informationen. Wenn diese Zeile eine andere Einrückung hat als die vorherige, beginnen wir eine neue Gruppe. Wenn es ist eine gespeicherte Zeile, schreiben Sie es aus, mit dem entsprechenden End - Tag am Ende der Zeile.

Wenn die aktuelle Zeile leer ist, drucken Sie sie einfach aus. Prüfen Sie, ob die aktuelle Einrückungsebene zu hoch ist, und sichern Sie die Kaution, falls dies der Fall ist. Wenn die aktuelle Vertiefung die gleichen wie die vorherigen ist, ist dies eine Fortsetzungszeile einer bereits gestartete Gruppe, so druckt nur die gespeicherte (zurück) Zeile und einen neuen bauen savedString, der die aktuelle Zeile mit ist die Breite der aktuellen Tag zwischen Einzug und Text eingefügt. Andernfalls beginnen wir eine neue Gruppe. Erstellen Sie also eine savedZeichenfolge, die die aktuelle Zeile ist, wobei das Anfangs-Tag (selbst) zwischen dem Einzug und dem Text eingefügt wird.

Wenn wir das Ende der Eingabe erreicht haben, beenden Sie die aktuelle Gruppe wie zuvor.