RegExps: Suchen und Ersetzen...
Patrick Andrieu
- perl
... von "baumustergleichen" Zeichenketten je Textzeile.
Hallo!
Als alles andere als ein Perl-Profi bin ich schnell mit meinem Latein am Ende. Jedoch wenn ich hierher komme, habe ich schon etliches ausprobiert.
Ich möchte für eine mit Hilfe eines Scripts generierte druckbare Version einer Seite die im Text vorkommenden Hyperlinks durch ihren lediglich fett formatierten und unterstrichenen Verweistext ersetzen. Zum Beispiel:
<a href="http://www.atomic-eggs.com/">Atomic Eggs</a> soll zu
<span class="fett_und_unterstrichen">Atomic Eggs</span> werden.
Die Datei wird zeilenweise gelesen:
open (TEXTFILE, $Pfad_zur_Datei) || print "Tja... :-)";
@TEXTLINES=<TEXTFILE>;
close (TEXTFILE);
foreach $TEXTLINES (@TEXTLINES) {
$TEXTLINES =~ s/<a(.*)href="(.*)">(.*)</a>/<span class="fett_und_unterstrichen">$3</span>/g;
print $TEXTLINES;
}
<a(.*)href... deswegen, weil hin und wieder (aber nicht immer) ein target="..." dabei ist :)
Nun, kommt nur ein Link in der Zeile vor, wird er ersetzt. Sind dagegen mehrere Links vorhanden, wird nur der letzte ersetzt, und der ganze Text zwischen dem ersten und der letzte Link verschwindet... was ja nach den von mir eingesetzten Platzhaltern logisch ist: er findet das erste <a [evtl. target] href, und ersetzt alles danach bis zum letzten vorkommenden Anführungszeichen und geschl. Klammer (">) durch $3...
Nur, wie mache ich es, dass alle "baugleichen", nur vom Ziel und Verweistext einander abweichende Hyperlinks, die in einer Zeile vorkommen, wie gewünscht ersetzt werden?
Viele Grüße aus Frankfurt/Main,
Patrick
Hallo Patrick,
Ich möchte für eine mit Hilfe eines Scripts
generierte druckbare Version einer Seite die im
Text vorkommenden Hyperlinks durch ihren
lediglich fett formatierten und unterstrichenen
Verweistext ersetzen. Zum Beispiel:<a href="http://www.atomic-eggs.com/">Atomic Eggs</a> soll zu
<span class="fett_und_unterstrichen">Atomic Eggs</span> werden.
[...]
$TEXTLINES =~ s/<a(.*)href="(.*)">(.*)</a>/<span class="fett_und_unterstrichen">$3</span>/g;
print $TEXTLINES;
}
Vergiss nicht, dass .* "greedy" ist. Es trifft
immer auf den laengsten moeglichen String. Besser
als der obere waere eher der hier:
$line =~ s~<a[^>]+href="[^"]+"[^>]*>([^<]+)</a>~<span class="fett_und_unterstrichen">$1</span>~sg;
Oder, falls im Link-Text auch Tags vorkommen
duerfen:
$line =~ s~<a[^>]+href="[^"]+"[^>]*>(.+?)</a>~<span class="fett_und_unterstrichen">$1</span>~sg;
Das Fragezeichen hinter dem Plus macht den Ausdruck
"ungreedy" statt "greedy". Naeheres zu den
Stichworten "greedy" und "ungreedy" erfaehrst du
auf http://perldoc.com/.
Gruesse,
CK
Hallo Christian!
Wie so oft, bist Du meine letzte Rettung (siehe counter.cgi *g*). Vielen Dank!
Es funktioniert jetzt wunderprächtig, nur würde ich es gerne verstehen :)
Zuerst war mir die form:
$line =~ s~~~parameter;
unbekannt. Ich kannte nur $line =~ s///parameter;.
Was ist der Unterschied? Es scheint so, als müsste man mit ~~~ die Sonderzeichen nicht mehr maskieren? Das verleiht dem Ganzen einen etwas leserlicheren Eindruck (sieht nicht so bekloppt aus wie sonst http://..... *g*) --> Sehe ich das richtig?
Greedy, ungreedy... ich gehe davon aus, dass es mit dem gierig/ungierig, wie es auf http://selfhtml.teamone.de/cgiperl/sprache/regexpr.htm#gierig_genuegsam beschrieben wird. Ich hatte vorher damit rumprobiert, leider erfolglos *g*
Wenn ich schon dabei bin, noch ein Frägche... Es geht um das Ersetzen (oder das gänzliche Entfernen) eines Formulars. Meistens gehen die ja über mehrere Zeilen hinweg. Beispiel:
<form name="mein_form" method="post" action="/cgi_bin/mein_mailer.pl">
<input type="hidden" name="recipient" value="an_mich">
<input type="hidden" name="subject" value="Kontakt">
<input type="hidden" name="redirect" value="meine_bestaetigungsseite">
<p>Name:<br>
<input type="text" name="realname" size="20" class="meine_css"><br>
E-Mail:<br>
<input type="text" name="email" size="20" class="meine_css"><br>
Mitteilung:<br>
<textarea class="meine_css" cols="20" rows="5" name="mitteilung"></textarea><br>
<input type="submit" class="button" value="Absenden"></p>
</form>
Mir gelingt es immer nur, lediglich die erste Zeile zu entfernen...
Gibt es eine Möglichkeit, alles zwischen DelimiterX und DelimiterY (hier im Beispiel <form und </form>) zu entfernen/ersetzen, auch wenn der Inhalt dazwischen sich über mehrere (auch eine unbekannte Anzahl von) Zeilen erstreckt?
Viele Grüße aus Frankfurt/Main,
Patrick
Nachtrag: der parameter m ist mir bekannt *g*
Viele Grüße aus Frankfurt/Main,
Patrick
Hi,
Nachtrag: der parameter m ist mir bekannt *g*
Mit dem müsste es funktionieren, wenn du das ganze HTML in *eine* Variable lädst. Wenn du es in ein Array packst (wie im Ursprungsposting), bleibt dir wohl nix anderes übrig, als beim Auftreten von <form.*?> dir die Zeilennnummer zu merken und beim Auftreten von </form> die Zeilen dazwischen rauszuwerfen.
HTH
wunderwarzenschwein
Hallo Patrick,
Wie so oft, bist Du meine letzte Rettung (siehe
counter.cgi *g*). Vielen Dank!
Gerne.
Zuerst war mir die form:
$line =~ s~~~parameter;
unbekannt. Ich kannte nur $line =~ s///parameter;.Was ist der Unterschied?
Der Unterschied sind die Delimiter ;)
Es scheint so, als müsste man mit ~~~ die
Sonderzeichen nicht mehr maskieren?
Doch, das muss man. Man hat nur andere
Delimiter-Zeichen, was in diesem Fall sinnvoll
ist, da dann / nicht mehr escaped werden muss.
Das verleiht dem Ganzen einen etwas
leserlicheren Eindruck (sieht nicht so bekloppt
aus wie sonst http://..... *g*) --> Sehe ich
das richtig?
Halb. Regular Expressions kann man in Perl in
jedes beliebige Zeichen einbauen. Ich haette auch
s{str}{repl} waehlen koennen oder s(str)(repl)
oder s!str!repl! oder sogar(!) s sStrsrepls. Perl
ist es schnuppe, welches Zeichen der
RegEx-Delimiter ist. Die intelligente Wahl
erleichtert uU die Lesbarkeit, weil dann weniger
escaped werden muss, z. B. muesste das / nicht
mehr maskiert werden.
Wenn ich schon dabei bin, noch ein Frägche...
Es geht um das Ersetzen (oder das gänzliche
Entfernen) eines Formulars. Meistens gehen die
ja über mehrere Zeilen hinweg. Beispiel:<form name="mein_form" method="post" action="/cgi_bin/mein_mailer.pl">
<input type="hidden" name="recipient" value="an_mich">
<input type="hidden" name="subject" value="Kontakt">
<input type="hidden" name="redirect" value="meine_bestaetigungsseite">
<p>Name:<br>
<input type="text" name="realname" size="20" class="meine_css"><br>
E-Mail:<br>
<input type="text" name="email" size="20" class="meine_css"><br>
Mitteilung:<br>
<textarea class="meine_css" cols="20" rows="5" name="mitteilung"></textarea><br>
<input type="submit" class="button" value="Absenden"></p>
</form>Mir gelingt es immer nur, lediglich die erste
Zeile zu entfernen...Gibt es eine Möglichkeit, alles zwischen
DelimiterX und DelimiterY (hier im Beispiel
<form und </form>) zu entfernen/ersetzen, auch
wenn der Inhalt dazwischen sich über mehrere
(auch eine unbekannte Anzahl von) Zeilen
erstreckt?
Klar.
s!<form[^>]*>.*?</form>!!s;
Der Modifier s ist hier noetig, damit der Punkt
auch auf ein Newline-Zeichen matcht. Das wuerde er
normalerweise naemlich nicht tun.
Gruesse,
CK
Hallo Christian!
Regular Expressions kann man in Perl in
jedes beliebige Zeichen einbauen. Ich haette auch
s{str}{repl} waehlen koennen oder s(str)(repl)
oder s!str!repl! oder sogar(!) s sStrsrepls. Perl
ist es schnuppe, welches Zeichen der
RegEx-Delimiter ist. Die intelligente Wahl
erleichtert uU die Lesbarkeit, weil dann weniger
escaped werden muss, z. B. muesste das / nicht
mehr maskiert werden.
$TEXTLINES =~ s zPatrick AndrieuzChristian Krusez; :)
Dann aber:
$TEXTLINES =~ s zEr schnarchtzEr macht \z\z\z\zz; , oder?
s!<form[^>]*>.*?</form>!!s;
Hmm... da haut's immer noch nicht hin. Form bleibt hartnäckig sichtbar... Ich habe mittlerweile eine einfache Testdatei erstellt, bei welcher das Formular nur aus:
<form>
test
</form>
besteht. Wird leider auch nicht entfernt.
Bei diesem Vorhaben ist es nicht so schlimm, da ich ja das kurze Form in einer Zeile schreiben kann, dann ist er ja weg. Aber beim AE-Forum kriege ich die Absendeformulare nie weg (es sei denn per Hand) - ich will es demnächst vom Netz nehmen und nur noch eine Sammlung der besten Postings anbieten. Auch wenn es nur so an die 50 Dateien sind, die übrigbleiben, wäre mir ein "automatisiertes" Entfernen via Script (da ich die Inhalte via Script bereits in andere Dateien übertragen will) natürlich viel lieber als händisch jedes einzelne <form .... /form> zu suchen!
Viele Grüße aus Frankfurt/Main,
Patrick
Hallo Patrick,
$TEXTLINES =~ s zPatrick AndrieuzChristian Krusez; :)
Dann aber:
$TEXTLINES =~ s zEr schnarchtzEr macht \z\z\z\zz; , oder?
Korrekt. Ich sehe, du hast es verstanden :)
s!<form[^>]*>.*?</form>!!s;
Hmm... da haut's immer noch nicht hin. Form
bleibt hartnäckig sichtbar...
Hast du mehrere Forms im Dokument und willst die
alle weg haben? Dann musst du den Modifier g
anhaengen. Die Datei liegt aber schon komplett in
einem Skalar und nicht zeilenweise in einem Array?
Du solltest die Datei so einlesen:
sub readfile {
my $file = shift;
local (*DAT,$/);
open DAT,'<'.$file or do {
warn "could not open '$file': $!";
return;
};
my $cnt = <DAT>;
close DAT and return $cnt;
return;
}
Gruesse,
CK
Hmm... da haut's immer noch nicht hin. Form bleibt hartnäckig sichtbar... Ich habe mittlerweile eine einfache Testdatei erstellt, bei welcher das Formular nur aus:
<form>
test
</form>
<style type="text/css" media="print">
form
{
display:none;
}
</style>
Ich weiss das das nicht die Diskussion ist, aber es ist die Lösung des Problems mit wesentlich geringeren Aufwand. Oder warum wurde meine Nachricht gelöscht? (oder habe ich sie gar nicht abgeschickt).
Struppi.
Hallo Struppi,
<style type="text/css" media="print">
form
{
display:none;
}
</style>
Eine Loesung, die unnoetigerweise Bandbreite
schluckt.
Gruesse,
CK
Hallo Struppi,
<style type="text/css" media="print">
form
{
display:none;
}
</style>Eine Loesung, die unnoetigerweise Bandbreite
schluckt.
Nicht unbedingt, im günstigsten Fall muss er das Dokument ja nicht mehr neu anfragen, die Styleshets sind ja schon geladen.
Ich halte die Lösung mit einem CGI Porgramm das ganze Dokument zu durchsuchen um es dann nochmal zu verschicken für wesentlich resourcenfressender.
Zumal für diesen Zweck.
Struppi.
Hallo Struppi,
<style type="text/css" media="print">
form
{
display:none;
}
</style>Eine Loesung, die unnoetigerweise Bandbreite
schluckt.Nicht unbedingt, im günstigsten Fall muss er
das Dokument ja nicht mehr neu anfragen, die
Styleshets sind ja schon geladen.
Die Dokumente muessen notwendigerweise immer wieder
neu angefragt werden. Irgendwann wird jeder Cache
erneuert.
Ich halte die Lösung mit einem CGI Porgramm das
ganze Dokument zu durchsuchen um es dann
nochmal zu verschicken für wesentlich
resourcenfressender.
Wer redet von einem CGI-Programm? Perl laeuft
problemlos auch auf der Shell. Einmal das Script
drueberlaufen lassen, fuer immer die geaenderten
Dateien.
Gruesse,
CK
Hallo Christian!
Wer redet von einem CGI-Programm? Perl laeuft
problemlos auch auf der Shell. Einmal das Script
drueberlaufen lassen, fuer immer die geaenderten
Dateien.
Naja, Struppi hat da schon recht: Der Aufruf der druckbaren Version wird schon online geschehen. Ich will keine vorbereiteten Printversionen der Seiten anbieten, weil ich davon ausgehe, dass nur die Seiten, die ganz oben in der Zähldateienstatistik meines Counters stehen, "evtl. mal gedruckt werden könnten". Dazu zählen die autorun- und Labels erstellen-Geschichte, sowie die compare.html (wobei saisonbedingt manchmal andere Seiten ganz oben stehen). Und wenn diese gedruckt werden, dann sicher um weitergegeben zu werden, und daher lege ich wert auf ein schönes Ergebnis, und daher kann sich das der User selber gestalten :)
Allerdings für das Archivieren der AE-Forums-Dateien werde ich schön lokal arbeiten, da ich auf dem Server alles mit einer neuen Struktur aufspielen werden - wenn's denn fertig wird :-) Dann werde ich mir Deine Lösung näher anschauen!
Viele Grüße aus Frankfurt/Main,
Patrick
Hallo Patrick,
Naja, Struppi hat da schon recht: Der Aufruf
der druckbaren Version wird schon online
geschehen.
Du willst das Formular nur zum Drucken entfernen?
Dann ist Struppies Loesung allemal sinnvoller. Du
kannst ja verschiedene Druck-Stylesheets anbieten.
Gruesse,
CK
Die Dokumente muessen notwendigerweise immer wieder
neu angefragt werden. Irgendwann wird jeder Cache
erneuert.
[....]
Wer redet von einem CGI-Programm? Perl laeuft
problemlos auch auf der Shell. Einmal das Script
drueberlaufen lassen, fuer immer die geaenderten
Dateien.
Da widersprichst du dir ja in einem Posting, erst soll das Dokument angefragt werden und dann passiert es doch nur auf dem Server? ja was denn nun.
Naja, wir Wissen ja mittlerweile das das gewünschte online passieren soll und ich seh ein, das Patrick experimentieren möchte, gerade für diese Aufgabe ist eventuell sogar das Modul HTML::Parser (oder ein anderes) sinnvoll, da das ganze nicht immer trivial ist.
Struppi.
Hallo Struppi,
Da widersprichst du dir ja in einem Posting,
erst soll das Dokument angefragt werden und
dann passiert es doch nur auf dem Server? ja
was denn nun.
Nein, du hast mich nur nicht verstanden. Es
spricht nichts dagegen, statische HTML-Dokumente
zu generieren.
Gruesse,
CK
Nein, du hast mich nur nicht verstanden. Es
spricht nichts dagegen, statische HTML-Dokumente
zu generieren.
Natürlich nicht, aber dann wird das Dokument zweimal übertragen, ob das jetzt wirklich Bandbreite spart gegenüber dem nicht übertragen des Print stylesheets hängt wohl stark vom Einzelfall ab.
Struppi.
Hallo Struppi!
Natürlich nicht, aber dann wird das Dokument zweimal übertragen, ob das jetzt wirklich Bandbreite spart gegenüber dem nicht übertragen des Print stylesheets hängt wohl stark vom Einzelfall ab.
Es wird vom Script eine reine Textdatei (die größte ist gerade mal 5 KB) gelesen und nach Änderung übertragen. Es wird schon etwas mehr Bandbreite verursachen als das übertragen eines etwa 1 KB großes Style-Sheet-File :)
Aber: ich möchte das einfach so. Punkt. ;-)
Viele Grüße aus Frankfurt/Main,
Patrick
Aber: ich möchte das einfach so. Punkt. ;-)
HTML::Parser.
Struppi.
Hallo Struppi!
HTML::Parser.
Was kann das Modul denn alles? Ich habe im Script bisher nur 4 Suchen/Ersetz-Zeilen:
Aus Anker-Links sollen Nummerierungen werden (<a href="#a1">Verweis</a> wird zu 1. Verweis und das entsprechende <a name="a1">Ankertext</a> wird zu <b>1. Ankertext</b>). Dann werden die Links im Fließtext durch deren Text ersetzt (war ja die Urprungsfrage des Postings) und bei der einen oder zwei Seiten, die ein <form>-Tag beinhalten, soll das (evtl... - ich überlege gerade noch was) ganz weg. Sonst bleibt alles wie es ist (alle <p>, <span> u.a. bleiben).
Die Lösung von Christian mit der anderen Art der Datei einzulesen ist nicht umsonst gewesen, denn die werde ich wie erwähnt für das Archivieren der Forumsdateien brauchen. Nur, die probiere ich ein anderes Mal, ich muss sehen, dass ich vorwärts komme - denn eigentlich hätte der Relaunch von AE am 27.08. stattfinden sollen :)
Viele Grüße aus Frankfurt/Main,
Patrick
Hallo Struppi!
HTML::Parser.
Was kann das Modul denn alles? Ich habe im Script bisher nur 4 Suchen/Ersetz-Zeilen:
Keine Ahnng.
Ich das Modul bisher erst einmal gebraucht habe, allerdings bin ich dann hier mit meiner alten Mühle (P100 16MB speicher) schnell an die Grenze gekommen, da ich aus ca. 1,500 Internetseiten Daten auslesen und Vergleichen mußte. Das hieß dann letztendlich, das ich alle Module wieder rausgeschmissen hatte und das Parsen selber gemacht habe, allerdings waren alle Dokumente gleich Strukturiert und ich brauchte nur spezielle Daten.
Struppi.
Hallo Struppi,
Natürlich nicht, aber dann wird das Dokument
zweimal übertragen, ob das jetzt wirklich
Bandbreite spart gegenüber dem nicht übertragen
des Print stylesheets hängt wohl stark vom
Einzelfall ab.
Mir war, wie gesagt, nicht bewusst, dass das fuer
Print-Versionen ist. In dem Fall ist das natuerlich
nicht sinnvoll, wie schon angemerkt :)
Gruesse,
CK
Hallo Struppi!
warum wurde meine Nachricht gelöscht? (oder habe ich sie gar nicht abgeschickt).
Vielleicht gehört Dein Posting zu denjenigen, die hier dabei waren: [pref:t=63804&m=362208]. Oder Du warst ein Opfer der Vorschau. Ich bin immer noch der Meinung, dass - wie damals im SCB - die Vorschau mit einem unübersehbaren, fetten Hintergrundbild versehen sein sollte:
<img src="http://www.atomic-eggs.com/selfspezial/vorschau.gif" border="0" alt="">
;-)
Ich weiss das das nicht die Diskussion ist, aber es ist die Lösung des Problems mit wesentlich geringeren Aufwand.
<style type="text/css" media="print">
form
{
display:none;
}
</style>
Es ist nicht so, dass ich mich mit CSS gar nicht auskenne, Struppi. Ich kenne auch den letzten Tipp-Trick-Artikel http://aktuell.de.selfhtml.org/tippstricks/css/drucklayout/ (habe ihn sogar gelesen, schon bevor er vorgestellt wurde)...
Nur, ich _will_ das einfach für dieses Vorhaben nicht einsetzen! Der Inhalt der neuen Atomic Eggs-Seiten steht in reinen Textdateien, die mittels SSI in die Seiten beim Aufruf eingebunden werden. Und es soll nur der Inhalt gedruckt werden können, sonst gar nichts. Es ist daher viel einfacher, diese Textdateien mit einem Perl-Script aus deren Verzeichnis zu "holen" und daraus eine Printversion anzubieten, als an einer CSS-Datei für den Druck zu friemen, die mit einigen Browsern vielleicht nicht die Ergebnisse erzielt, die gewünscht sind. Bis auf die Problemchen mit dem Ersetzen der Links und des Formulars, wo Christian mir eben hilft, war alles in 3 mn fertig. Und so wie ich es jetzt mache, bin ich einigermassen sicher, dass das (druckbare) Ergebnis mit allen Browsern gleich aussieht. Weil: das Ergebnis wird vom Server ausgeliefert, und ist nicht abhängig von der unterschiedlichen CSS-Interpretation der möglichen User-Clients.
Viele Grüße aus Frankfurt/Main,
Patrick
... von "baumustergleichen" Zeichenketten je Textzeile.
Hallo!
Als alles andere als ein Perl-Profi bin ich schnell mit meinem Latein am Ende. Jedoch wenn ich hierher komme, habe ich schon etliches ausprobiert.
Ich möchte für eine mit Hilfe eines Scripts generierte druckbare Version einer Seite die im Text vorkommenden Hyperlinks durch ihren lediglich fett formatierten und unterstrichenen Verweistext ersetzen. Zum Beispiel:
<a href="http://www.atomic-eggs.com/">Atomic Eggs</a> soll zu
<span class="fett_und_unterstrichen">Atomic Eggs</span> werden.
Warum verwendest du nicht einfach ein Druck Stylesheet, dass aus Links eben genau das macht
@media print
{
a{
text-decoration:underline;
font-weight:bold;
}
}
Dann sparst du dir 'ne ganze Menge Arbeit und wenn du das Konsequent nutzt auch einen zusätlichen Aufruf deiner Seite.
Struppi.
Hallo Struppi!
Warum verwendest du nicht einfach ein Druck Stylesheet
Warum wußte ich, dass mich jemand diese Frage stellen würde? :)
Aber ich will sie gerne beantworten: Die Seiten beinhalten ein Logo und einige Grafiken, die nicht gedruckt werden sollen. Der User soll nur den reinen Textinhalt drucken und mit diesem kleinen Programm, kann er jetzt seinen Ausdruck selbst formatieren: Er kann die Schriftart- und die Schriftfarbe wählen, kann wählen, ob die im Text der Webseite vorkommenden Hyperlinks auf dem Ausdruck unterstrichen, fett oder einfach ohne Formatierung erscheinen sollen uvm... :)
Viele Grüße aus Frankfurt/Main,
Patrick
Warum verwendest du nicht einfach ein Druck Stylesheet
Warum wußte ich, dass mich jemand diese Frage stellen würde? :)
Naja, ist ja naheliegend
Aber ich will sie gerne beantworten: Die Seiten beinhalten ein Logo und einige Grafiken, die nicht gedruckt werden sollen. Der User soll nur den reinen Textinhalt drucken und mit diesem kleinen Programm, kann er jetzt seinen Ausdruck selbst formatieren: Er kann die Schriftart- und die Schriftfarbe wählen, kann wählen, ob die im Text der Webseite vorkommenden Hyperlinks auf dem Ausdruck unterstrichen, fett oder einfach ohne Formatierung erscheinen sollen uvm... :)
Liesse sich ja alles mit CSS regeln. Die Frage ist ob sowas überhaupt genutzt wird.
Normalerweise druckt man ja eine Internetseiten nur mal eben schnell aus um den Inhalt in schriftlicher Form in er Hand zu haben, ich zumindest würde mir dann aber nicht die Arbeit machen, dann noch rumsuchen welche Einstellungen du mir anbietest. Kann aber in deinem speziellen Fall anders sein. Aber wie gesagt ich würde mir erstmal überlegen, wie du das selber handhabst und ob du solch ein Feature jemals irgendwo genutzt hast.
Mir wäre sowas zuviel Aufwand.
Struppi.