Problemstellung:
Eine Eingabedatei enthält Text, der mit null oder mehr Tabulatorzeichen eingerückt ist. Im Einzelnen ist jede Zeile in der Eingabe eine der folgenden:
- Leer oder
- Null oder mehr Tabulatoren (bis zu einer Grenze; siehe unten), gefolgt von einem Zeichen, das weder ein Leerzeichen noch ein Tabulator ist (gefolgt von null oder mehr Zeichen).
Es gibt keine Zeilen, die
- Beginnen Sie mit null oder mehr Tabulatoren, gefolgt von einem Leerzeichen. (Dies impliziert, dass es keine Zeilen gibt, die mit einem Leerzeichen beginnen.)
Oder
- Besteht ausschließlich aus einer oder mehreren Registerkarten (und nichts anderem).
oder
- Beginnen Sie mit mehr als einer festgelegten Anzahl von Registerkarten.
Die Eingabe wird logisch in Gruppen von Zeilen zerlegt, die alle entweder sind
- Leer oder
- Mit der gleichen Anzahl von Tabulatoren eingerückt.
Leerzeilen werden unverändert an den Ausgang durchgereicht.
Es ist eine Liste von Tags anzugeben. zB x
, y
und zz
. Eine Gruppe von (nicht leeren) Zeilen, die mit Null-Tabulatoren eingerückt sind (dh nicht eingerückt sind), muss zwischen <x>
und stehen </x>
. Eine Gruppe von Zeilen, die mit einem Tabulator eingerückt sind, muss in <y>
und stehen </y>
. Eine Gruppe von Zeilen, die mit zwei Tabulatoren eingerückt sind, muss zwischen <zz>
und stehen </zz>
. (Zeilen werden nicht mit mehr als zwei Tabulatoren eingerückt.)
In der ersten Zeile einer Gruppe (von nicht leeren Zeilen) muss das Anfangsetikett zwischen den Tabulatoren und dem Text eingefügt werden. In der letzten Zeile einer Gruppe muss das End-Tag am Ende des Textes angehängt werden. Eine Gruppe kann aus einer einzelnen Zeile bestehen, sodass die erste Zeile auch die letzte Zeile sein kann. Alle Zeilen einer anderen Gruppe als der ersten werden zusätzlich um die Breite des Anfangstags eingerückt (mit Leerzeichen zwischen den Tabulatoren und dem Text).
Zum Beispiel (mit ―→
, um eine Registerkarte darzustellen), diese EINGABE :
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:
Offensichtlich wissen wir nicht genau, was wir mit einer Eingabezeile tun sollen, bis wir die nächste Zeile gelesen haben. Dieses Problem wird normalerweise behoben, indem der Inhalt einer zu verarbeitenden Zeile gespeichert wird, nachdem die nächste 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
, y
und zz
) durch Aufteilen einer durch Leerzeichen getrennten Zeichenfolge. Das tag_pad
Array enthält genügend Leerzeichen, um mit der Breite der Tags übereinzustimmen (einschließlich <
und >
):
tag_pad[1]
und tag_pad[2]
drei Leerzeichen; tag_pad[3]
ist vier Leerzeichen.
Wenn wir eine Eingabezeile lesen, analysieren wir sie. Wenn es keine Felder ( NF == 0
) enthält, muss es leer sein (da wir angegeben haben, dass keine Zeile ausschließlich aus Leerzeichen und Tabulatoren besteht). Setzen Sie es indent_num
auf 0. Anderenfalls messen Sie den Einzug, indem Sie die Position von $1
(dem ersten Wort) in $0
(ermitteln. die gesamte Zeile).
index
Gibt einen Wert ab 1 zurück. Dies ist also eine Stelle mehr als die Anzahl der Leerzeichen vor dem ersten Nicht-Leerzeichen (und wir gehen davon aus, dass dies alle Registerkarten sind). Das ist ein Glücksfall, denn jetzt indent_num
entspricht es den Einträgen in den Arrays tags
und tag_pad
. Dann teilen wir die Zeile in ein indent_str
(Leerzeichen) und ein restOfLine
(alles nach dem Einzug).
Jetzt verlassen wir uns auf gespeicherte Informationen. Wenn diese Zeile einen anderen Einzug 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. Überprüfen Sie, ob die aktuelle Einrückungsstufe zu hoch ist, und sichern Sie sie, falls dies der Fall ist. Wenn der aktuelle Einzug mit dem vorherigen identisch ist, handelt es sich um eine Fortsetzung einer bereits gestarteten Gruppe. Drucken Sie daher einfach die gespeicherte (vorherige) Zeile und erstellen Sie eine neue saved
Zeichenfolge, die der aktuellen Zeile mit der Breite des aktuellen Einzugs entspricht Tag, das zwischen dem Einzug und dem Text eingefügt wird. Andernfalls starten wir eine neue Gruppe. Erstellen Sie daher eine saved
Zeichenfolge, die die aktuelle Zeile mit dem Starttag (selbst) zwischen dem Einzug und dem Text darstellt.
Wenn wir am Ende der Eingabe angelangt sind, beenden Sie die aktuelle Gruppe wie zuvor.
<b>
Strophen.