Fehlerhafte (nicht abgeschlossene) HTML Tags entfernen
Christian_
- php
Hallo,
ich bin auf der Suche nach einem Skript, dass fehlerhafte HTML Tags aus einem String entfernt. Zur Verdeutlichung ein Beispiel:
Aus dem String 'ABC, 123, <img src="images/bild.'
soll werden 'ABC, 123, '
Im Internet habe ich diverse Skripte gefunden, die nicht geschlossene Tags abschließen, in diesem Fall hilft mir das jedoch wenig, da ich nicht möchte, dass beispielsweise, fehlerhafte Bilder angezeigt werden.
Mein gedanklicher Ansatz ist folgender:
1. In dem String muss der erste offene Tag gesucht werden.
2. Es muss überprüft werden ob nach diesem ersten offenen Tag weitere Tags geöffnet wurden bevor der erste Tag geschlossen wurde.
2.1. Ist das der Fall wird erst nach dem zweiten offenen Tag gesucht und geschaut ob nach dem weitere Tags geöffnet werden bevor es geschlossen wird. (Das wird ein rekursiver Aufruf)
Wird dann ein offener Tag geschlossen, beendet sich ein rekursiver Aufruf und das Skript geht wieder einen Tag zurück und überprüft ob der Tag geschlossen wird.
Sollte der ursprüngliche Tag nicht geschlossen werden, wird alles ab dort gelöscht.
3. Es wird nach dem zweiten offenen Tag gesucht, nachdem der erste geschlossen wurde.
usw...
Ich hoffe mir kann dabei jemand helfen.
Liebe Grüße
Christian
Hallo nochmal,
anscheinend habe ich nicht genau genug gesucht, ich habe doch mehrere Ergebnisse im Netz gefunden. Leider hat keiner dazu eine passende Lösung gefunden. Hier ein paar Aufzählung von Links die sich mit dem Thema beschäftigen:
http://www.easy-coding.de/text-logisch-abschneiden-t4268.html
http://forum.de.selfhtml.org/archiv/2009/2/t182831/
http://www.php.de/php-einsteiger/43376-text-kuerzen-kein-problem-aber-mit-html.html <- hier wird sogar ein Parser vorgestellt, der aber leider nicht richtig funktioniert.
Liebe Grüße
Christian
Hallo Christian,
Dein Problem lässt sich relativ einfach mit den üblichen String-Funktionen beheben. Erst teilst Du den String mit explode() bei jedem '<'
auf. Du erhältst ein array, welches Du mit einer Schleife und strpos() nach '>'
durchsuchen kannst. Ist es nicht enthalten, wird das Array-Element gelöscht.
Dieses recht simple Vorgehen hat allerdings auch zwei Schönheitsfehler. Zum einen schlägt das ganze bei Kommentaren, die ihrerseits Elemente umschließen (so z. B. <!-- <br/> -->
), fehl. Zum anderen kannst Du ohne erheblichen Mehraufwand nicht feststellen, wo das fehlerhaft notierte Element endet, was nach der obigen Vorgehensskizze und einem String '<img src="images/bild. ABC, 123, '
zum Verlust von Daten führt (in dem Fall ' ABC, 123, '
).
Gruß aus Berlin!
eddi
@@Christian_:
nuqneH
ich bin auf der Suche nach einem Skript, dass fehlerhafte HTML Tags aus einem String entfernt.
Was ist dein Problem, das du damit lösen willst?
Qapla'
Hallo,
danke Edgar, ich habe mal etwas versucht. Leider funktioniert es noch nicht ganz. Was mache ich falsch?
function checktags($string, $index = 0) {
$opentagpos = strpos($string,'<', $index);
$index = $opentagpos + 1;
$closetag1pos = strpos($string,'</',$index);
$closetag2pos = strpos($string,'>',$closetag1pos);
if($closetag1pos !== false and $closetag2pos !== false) {
$inneropentag = strpos($string, '<', $index);
if(($inneropentag < $closetag1pos)) {
$array = checktags($string, $inneropentag);
$closetag1pos = strpos($array[0],'</',$array[1]);
$closetag2pos = strpos($array[0],'>',$closetag1pos);
$inneropentag = strpos($array[0], '<', $array[1]);
if($closetag1pos === false and $closetag2pos === false)
return array(substr($array[0],$opentagpos-1),$opentagpos-1);
elseif($inneropentag === false)
return array($array[0]);
else
checktags($array[0],$inneropentag);
} else {
return array($string, $closetag2pos);
}
} else
return array(substr($string,$opentagpos-1),$opentagpos-1);
}
$string = '<a><b></b></a>';
$array = checktags($string);
echo $array[0];
Hi!
ich habe mal etwas versucht. Leider funktioniert es noch nicht ganz. Was mache ich falsch?
*seufz* Zwei Dinge werden ewig bestehen bleiben: "Funktioniert nicht"-Fehlerbeschreibungen und unkommentierter Code.
Was konkret erwartest du bei welchen Eingabedaten, was erhältst du stattdessen als Ergebnis und an welcher Stelle genau haben welche Variablen andere Werte (oder Ausdrücke andere Ergebnisse) als von dir vorgesehen?
Lo!
Hallo,
auch wenn es eine umständlcieh Lösung ist, sie funktioniert:
// Der Index bestimmt die aktuelle Prüfposition im String
function checktags($string, $index = 0) {
// Position des ersten Tags '<' Bestimmen
$opentagpos = strpos($string,'<', $index);
// Wenn ein öffnendes Tag vorhanden ist
if($opentagpos !== false) {
$index = $opentagpos + 1;
// Position der ersten Schluss Tags '</' und '>' bestimmen
$closetag1pos = strpos($string,'</',$index);
$closetag2pos = strpos($string,'>',$closetag1pos);
// Position des ersten selbstschließenden Schlusstags bestimmen
$selfclosetag = strpos($string,'/>',$index);
// Wenn ein Schlusstag vorhanden ist
if($selfclosetag !== false or (($closetag1pos !== false) and ($closetag2pos !== false))) {
// Position des nächsten offenen Tags '<' bestimmen
$inneropentag = strpos($string, '<', $index);
// Abfrage nach erstem Schluss-Tag: '>' oder '/>'
if($selfclosetag !== false and $selfclosetag < $closetag2pos)
$firstclosedtag = $selfclosetag;
else // firstclosedtag wird auf das Erste schließende Tag gesetzt
$firstclosedtag = $closetag2pos;
// Position des nächsten offenen Tags nach dem ersten schließenden Tag bestimmen
$nexttag = strpos($string,'<',$firstclosedtag);
// Wenn das nächste offene Tag noch vor dem ersten schließenden Tag vorkommt. Bsp: <a><b></b></a> oder <a><img /></a>
// ^ ^
if(($inneropentag < $closetag1pos and $firstclosedtag == $closetag2pos) or ($inneropentag < $selfclosetag and $firstclosedtag == $selfclosetag)) {
$array = checktags($string, $inneropentag); // Dann erst das verschachtelte offene Tag überprüfen -> Rekursiver Selbstaufruf
$closetag1pos = strpos($array[0],'</',$array[1]); // Anschließend überprüfen ob das umfassende Tag geschlossen ist
$closetag2pos = strpos($array[0],'>',$closetag1pos);
$nexttag = strpos($array[0], '<',$closetag2pos); // Position des nächsten Tags nach dem Schluss-Tag des umfassenden Tags bestimmen
if($closetag1pos === false or $closetag2pos === false) // Sollte das umfassende Tag nicht geschlossen sein wird der String ab der Position
if($opentagpos == 0) // des umfassenden Tags gekürzt
return array('',0);
else
return array(substr($array[0],0,$opentagpos),$opentagpos-1);
elseif($nexttag !== false and $nexttag!== strpos($array[0],'</',$closetag2pos)) // Sollte ein weiteres Tag nach dem Schluss-Tag folgen
return checktags($array[0],$nexttag); // wird es überprüft -> Rekursion
else
return array($array[0], $closetag2pos); // Sonst wird der String ausgegeben
// Sollte kein verschachteltes Tag vorkommen, wird überprüft ob nach dem ersten Schlusstag ein weiteres offenes Tag folgt
} elseif($nexttag !== false and $nexttag !== strpos($string,'</',$firstclosedtag))
return checktags($string,$nexttag); // Falls ja, wird es überprft
else
return array($string, $firstclosedtag+1); // Sonst wird der String zürckgegeben
// Sollte es kein Schluss-Tag geben wird der String ab der Position des offenen Tags gelöscht.
} elseif($opentagpos == 0)
return array('',0);
else
return array(substr($string,0,$opentagpos),$opentagpos-1);
// Wenn kein öffnendes Tag vorhanden ist -> String ausgeben ohne Änderung
} else
return array($string);
}
Ich hoffe es ist ausreichend kommentiert.
Gruß
Christian
Hallo Christian,
function checktags($string, $index = 0) {
$opentagpos = strpos($string,'<', $index);
$index = $opentagpos + 1;$closetag1pos = strpos($string,'</',$index);
$closetag2pos = strpos($string,'>',$closetag1pos);
bereits hier machst Du Dir unnötige Umstände. Es reicht aus, die erste und zweite Postion von '<' zu ermitteln und nur diese Teilzeichenkette zu betrachen:
`$segment=substr($string,($p=strpos($string,'<')+1),strpos($string,'<',$p)-$p);`{:.language-php}
In `$segment`{:.language-php} muss denn nur noch nach einem '>' gesucht werden.
So würde man dann Teilzeichenkette für Teilzeichenkette abarbeiten, nur warum sollte man das tun? explode() nimmt einem diese Arbeit ganz komfortabel ab:
~~~php
$array=explode('<',$string);
foreach($array as $v)
if(strpos($v,'>')===false)
echo "fehlerhaften Tag gefunden in '$v'\n";
if($closetag1pos !== false and $closetag2pos !== false) {
$inneropentag = strpos($string, '<', $index); if(($inneropentag < $closetag1pos)) { $array = checktags($string, $inneropentag); $closetag1pos = strpos($array[0],'</',$array[1]); $closetag2pos = strpos($array[0],'>',$closetag1pos); $inneropentag = strpos($array[0], '<', $array[1]); if($closetag1pos === false and $closetag2pos === false) return array(substr($array[0],$opentagpos-1),$opentagpos-1); elseif($inneropentag === false) return array($array[0]); else checktags($array[0],$inneropentag);
Bei Deinem String `'<a><b></b></a>'`{:.language-php} greift Deine erste Bedingung `($closetag1pos === false and $closetag2pos === false)`{:.language-php} nicht. Auch Deine zweite Bedingung `($inneropentag === false)`{:.language-php} greift nicht. Es wird also getan, was unter `else`{:.language-php} angegeben ist. Vermutlich muss es da `return checktags($array[0],$inneropentag);`{:.language-php} heißen. So jedenfalls wird nichts ausgegeben. Testausgaben, so z. B. mit `print_r(array($closetag1pos,$closetag2pos,$inneropentag));`{:.language-php}, vor den Bedingungen hätten Dir hier helfen können, den Flüchtigkeitsfehler zu finden.
Aber wie geschrieben, halte ich Deine Ansatz für umständlich und auch nicht wirklich zielführend, wenn ich mir die Ausgabe mit dem eingefügten `return`{:.language-php} ansehe. Nutze lieber explode()!
Gruß aus Berlin!
eddi