Fried-rich
Anmeldungsdatum: 2. Mai 2013
Beiträge: 1093
|
Hallo, ich kann damit sed 's|<.[^>]*>||g' alle HTML-Tags aus einer Datei löschen lassen. Ist es möglich eine Ausnahme einzufügen, so dass alle Tags außer sagen wir "<body>" und "</body>" gefunden und gelöscht werden? Mein bisher einzige Idee ist, die entsprechenden Tags vorher umzubenennen und hinterher wieder zurück. Friedrich
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12829
|
So etwas würde man besser mit einem Tool machen, das für die Verarbeitung von tagbasierten Sprachen gebaut ist, wie z.B. xmlstarlet . Mit regulären Ausdrücken braucht man negative Vorschau, damit der Ausdruck beherrschbar bleibt (Zeile 5): | $ cat data.html
a<b>c<d>e
$ ruby -pe 'gsub(/<[^<>]*>/,"")' data.html
ace
$ ruby -pe 'gsub(/<(?!b>)[^<>]*>/,"")' data.html
a<b>ce
|
Man kann auch mit der Option -i arbeiten wie bei sed und eine Ersetzung an der Stelle machen.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
Dafür ist sed bestens geeignet:
| sed -r "/<\/?body>/n; s|<.[^>]*>||g" beispiel.html
|
Der n-Befehl hüpft zur nächsten Eingabezeile.
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12829
|
user_unknown schrieb: Dafür ist sed bestens geeignet:
| sed -r "/<\/?body>/n; s|<.[^>]*>||g" beispiel.html
|
Der n-Befehl hüpft zur nächsten Eingabezeile.
Funktioniert das auch, wenn mehrere Tags in einer Zeile sind?
|
shiro
Anmeldungsdatum: 20. Juli 2020
Beiträge: 962
|
rklm schrieb: Funktioniert das auch, wenn mehrere Tags in einer Zeile sind?
Natürlich nicht. Wenn man die Aufgabe nicht mit ruby oder gawk sondern mit sed lösen will, sollte man mit einem "Hilfszeichen" (z.B.°) agieren:
echo -e "<html>test\n<meta>mehr</meta><title>Ein Titel</title>\n<body>auch test</body>fin</html>" |
sed -r "s#<[/]*(body|meta)#°&°#g;s#<.[^>°]*>##g;s/°//g"
test
<meta>mehr</meta>Ein Titel
<body>auch test</body>fin
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
rklm schrieb: user_unknown schrieb: Dafür ist sed bestens geeignet:
| sed -r "/<\/?body>/n; s|<.[^>]*>||g" beispiel.html
|
Der n-Befehl hüpft zur nächsten Eingabezeile.
Funktioniert das auch, wenn mehrere Tags in einer Zeile sind?
Nein, aber in der Praxis hat man es ja oft mit generischem HTML zu tun, das immer wieder von der gleichen Quelle kommt und sucht nicht eine Lösung, die auf alles passt. shiro schrieb: rklm schrieb: Funktioniert das auch, wenn mehrere Tags in einer Zeile sind?
Natürlich nicht. Wenn man die Aufgabe nicht mit ruby oder gawk sondern mit sed lösen will, sollte man mit einem "Hilfszeichen" (z.B.°) agieren:
echo -e "<html>test\n<meta>mehr</meta><title>Ein Titel</title>\n<body>auch test</body>fin</html>" |
sed -r "s#<[/]*(body|meta)#°&°#g;s#<.[^>°]*>##g;s/°//g"
test
<meta>mehr</meta>Ein Titel
<body>auch test</body>fin
Funktioniert das denn auch mit <BODY> ?
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12829
|
shiro schrieb:
Wenn man die Aufgabe nicht mit ruby oder gawk sondern mit sed lösen will, sollte man mit einem "Hilfszeichen" (z.B.°) agieren:
echo -e "<html>test\n<meta>mehr</meta><title>Ein Titel</title>\n<body>auch test</body>fin</html>" |
sed -r "s#<[/]*(body|meta)#°&°#g;s#<.[^>°]*>##g;s/°//g"
test
<meta>mehr</meta>Ein Titel
<body>auch test</body>fin
Der Ansatz ist nicht sehr robust, insbesondere, weil er das Sonderzeichen außerhalb von Tags löscht. Dann lieber so: | $ cat data.html
a<b>c<d>e
<b>c<d>e
<d>e
$ sed 's#</\?b>#@&#g;s#\(^\|[^@]\)</\?[^<>]*>#\1#g;s#@\(</\?b>\)#\1#g' data.html
a<b>ce
<b>ce
e
|
|
shiro
Anmeldungsdatum: 20. Juli 2020
Beiträge: 962
|
user_unknown schrieb: Funktioniert das denn auch mit <BODY> ?
Wow... Qualitätsverbesserungsmaßnahmen. Ja, wenn man beim sed den "I" Schalter (case insensitive) nutzt. Also:
echo -e "<html>test\n<meta>mehr</meta><title>Ein Titel</title>\n<BODY>auch test</body>fin</html>" |
sed -r "s#<[/]*(body|meta)#°&°#Ig;s#<.[^>°]*>##g;s/°//g"
test
<meta>mehr</meta>Ein Titel
<BODY>auch test</body>fin PS: Hab grad gesehen, dass rklm parallel auch geantwortet hat. Ja, die Nutzung von "Hilfszeichen" ist nicht sonderlich robust. Die Nutzung eines Zeichens (bei dir @) ist etwas besser. Aber wo werden bei deinem Vorschlag die mehreren <tag>..</tag> Klammern (nicht nur <b>..</b>) behandelt?
Wenn ich meinen Input nehme, kommt dabei folgendes Ergebnis raus:
echo -e "<html>test\n<meta>mehr</meta><title>Ein Titel</title>\n<BODY>auch test</body>fin</html>" |
sed 's#</\?body>#@&#Ig;s#\(^\|[^@]\)</\?[^<>]*>#\1#g;s#@\(</\?body>\)#\1#Ig'
test
mehr<title>Ein Titel
<BODY>auch test</body>fin
Der Token <title> gleich nach </meta> wird mit einem "@" nicht erkannt (er macht kein @<title>).
|
san04
Anmeldungsdatum: 19. Januar 2010
Beiträge: 1074
|
OT rklm schrieb: $ sed 's#</\?b>#@&#g;s#\(^\|[^@]\)</\?[^<>]*>#\1#g;s#@\(</\?b>\)#\1#g' data.html
Respekt! Schreibst du sowas aus dem Kopf zusammen, oder gibts da irgendwelche Hilfstools für? Für mich sieht das immer so aus als wäre jemand auf der Tastatur eingeschlafen 😛 /OT
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12829
|
san04 schrieb:
rklm schrieb: $ sed 's#</\?b>#@&#g;s#\(^\|[^@]\)</\?[^<>]*>#\1#g;s#@\(</\?b>\)#\1#g' data.html
Schreibst du sowas aus dem Kopf zusammen, oder gibts da irgendwelche Hilfstools für? Für mich sieht das immer so aus als wäre jemand auf der Tastatur eingeschlafen 😛
😀 Nein, natürlich nicht. Mein "Trick" ist, eine Testdatei zu haben, die möglichst viele relevante Fälle abdeckt und diese Ausdrücke nach und nach zu entwickeln - also mit einem einfachen anzufangen und den Zug um Zug zu erweitern und immer zu testen. Dann sieht man nach jedem Schritt, ob dabei herauskommt, was man beabsichtigt hat. Und es hilft, wenn man eine Idee hat, was man am Ende realisieren möchte. In diesem Fall waren das drei Schritte: Alle zu erhaltenden Tags mit einem Präfix versehen (hier "@"). Alle Tags löschen, die diesen Präfix nicht haben. (Achtung, das schließt auch Tags ein, die direkt am Anfang der Zeile stehen!) Alle in Schritt 1 eingefügten Präfixe wieder entfernen.
Für "b" muss man natürlich einen Ausdruck einsetzen, der die gewünschten Tagnamen erfasst. Im aktuellen Fall finde ich die Lösung mit negativer Vorschau allerdings deutlich eleganter. Ich hatte noch den schließenden Tag vergessen. Also: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | $ cat data.html
a<b>c<d>e
<b>c<d>e
<d>e
a</b>c</d>e
</b>c</d>e
</d>e
$ ruby -pe 'gsub(/<(?!\/?b>)[^<>]*>/,"")' data.html
a<b>ce
<b>ce
e
a</b>ce
</b>ce
e
$ ruby -pe 'gsub(%r{<(?!/?b>)[^<>]*>},"")' data.html
a<b>ce
<b>ce
e
a</b>ce
</b>ce
e
|
|
san04
Anmeldungsdatum: 19. Januar 2010
Beiträge: 1074
|
Okay, aber eben doch per Hand. Schon stark 👍
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12829
|
san04 schrieb: Okay, aber eben doch per Hand.
Ja, das stimmt. "Mastering Regular Expressions" von O'Reilly ist ganz hilfreich. Und, was auch total toll ist, ist der Regex Coach. Läuft zum Glück auch unter Wine (zumindest das letzte Mal, als ich es vor Jahren probiert habe) und erlaubt genau zu verstehen, wie ein Match funktioniert, weil man damit praktisch alle Schritte wie in einem Debugger durchgehen kann.
|
shiro
Anmeldungsdatum: 20. Juli 2020
Beiträge: 962
|
rklm schrieb: Der Ansatz ist nicht sehr robust, insbesondere, weil er das Sonderzeichen außerhalb von Tags löscht.
Die Bemerkung hat mir keine Ruhe gelassen. Daher hier die robuste Variante, die die Tags "body" oder "meta" stehen lässt:
echo -e "<html>test\n<meta>mehr</meta><title>Ein 1°C Titel</title>\n<BODY>auch test</body>fin</html>" |
sed -r "s#<[/]*(body|meta)#°&°#Ig;s#<.[^>°]*>##g;s/°([<>][^°]*)/\1/g"
test
<meta>mehr</meta>Ein 1°C Titel
<BODY>auch test</body>fin
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
shiro schrieb: rklm schrieb: Der Ansatz ist nicht sehr robust, insbesondere, weil er das Sonderzeichen außerhalb von Tags löscht.
Die Bemerkung hat mir keine Ruhe gelassen. Daher hier die robuste Variante, die die Tags "body" oder "meta" stehen lässt:
echo -e "<html>test\n<meta>mehr</meta><title>Ein 1°C Titel</title>\n<BODY>auch test</body>fin</html>" |
sed -r "s#<[/]*(body|meta)#°&°#Ig;s#<.[^>°]*>##g;s/°([<>][^°]*)/\1/g"
test
<meta>mehr</meta>Ein 1°C Titel
<BODY>auch test</body>fin
Mein Einwand mit BODY war nicht ganz ernst gemeint. Suchen/ersetzen kann sich als Aufgabe auf 3 Weisen stellen: a) ad hoc, als Einzelfall. Man sieht ein Problem und erbastelt eine Lösung, die auf den speziellen Fall zugeschnitten ist. Da sieht man dann schon, ob ein Erstezungszeichen funktioniert oder nicht. b) als wiederkehrende Aufgabe für ein Format - eine Webseite, deren Inhalt aktualisiert wird, die aber, da generisch erzeugt, von Struktur und Format lange Zeit stabil ist. Auch hier kann eine Bastellösung funktionieren. c) Als endgültige Universallösung für alles und jedes. Wenn man jede Eventualität berücksichtigen will, wird man nie fertig. Das sprengt erstens die Antwortmöglichkeiten im Forum und so viel Zeit hat niemand. Falls Du meinst, c schon erreicht zu haben, dann schlage ich vor, | <html>test
<meta>mehr</meta><title font="xuru">Ein 1°C Titel</title>
<BODY>auch test</body>fin</html>
|
als nächste Übung zu absolvieren, bevor wir mit | <html><!-- test "<body> body <b> fett </body> "
<meta>mehr --> <meta> mehr </meta><title font="xuru">Ein 1°C Titel</title>
<BODY>auch test</body>fin</html>
|
schwieriges Terrain betreten. ☺
|
shiro
Anmeldungsdatum: 20. Juli 2020
Beiträge: 962
|
user_unknown schrieb: Suchen/ersetzen kann sich als Aufgabe auf 3 Weisen stellen:
Ich gehe mit deiner Eingliederung konform. Ich schreibe, wie rklm schon beschrieben hat, die regex auch "einfach drauf los". Das ist bei einfachen Aufgaben schnell gemacht, bei komplexeren führt das dann schnell zu schwierig lesbaren Konstrukten (siehe Bemerkung von san04). Daher werde ich auch nur noch auf die beiden Beispiele von dir eingehen 😉 <html>test
<meta>mehr</meta><title font="xuru">Ein 1°C Titel</title>
<BODY>auch test</body>fin</html>
Das funktioniert bereits mit der von mir zuvor gezeigten regex ohne Änderung:
echo '<html>test
<meta>mehr</meta><title font="xuru">Ein 1°C Titel</title>
<BODY>auch test</body>fin</html>' |
sed -r "s#<[/]*(body|meta)#°&°#Ig;s#<.[^>°]*>##g;s/°([<>][^°]*)/\1/g"
test
<meta>mehr</meta>Ein 1°C Titel
<BODY>auch test</body>fin
Komplizierter wird es bei deinem 2. Beispiel, da der "<!– –>" Kommentar über mehrere Zeilen geht. Daher kommt zuvor ein rabiater Kommentar-Entferner hinzu:
echo '<html><!-- test "<body> body <b> fett </body> "
<meta>mehr --> <meta> mehr </meta><title font="xuru">Ein 1°C Titel</title>
<BODY>auch test</body>fin</html>' |
sed -r "s/<\!--.*(-->|$)//g;s/.*-->//g;s#<[/]*(body|meta)#°&°#Ig;s#<.[^>°]*>##g;s/°([<>][^°]*)/\1/g"
<meta> mehr </meta>Ein 1°C Titel
<BODY>auch test</body>fin
Ja, für geschachtelte Kommentare ist der Ansatz zu einfach. Aber damit will ich es bestehen lassen (s.o).
|