Newsgroup: it.comp.www.php
Titolo: cambiare <br> con <p></p>

Usando DOM c'� qualcuno in rete che ha scritto un codice php abbastanza
intelligente da cambiare i break con i pragraph?
 
Per esempio:
 
aaaaaa<br>
bbbbbbbb
cccccccc
<br><br>
dddddddd
eeeeeeee
<blockquote>
ffffffff
gggggggg
<br><br>
hhhhhhhh
</blockquote>
 
Convertirlo in:
 
<p>aaaaaa<br>
bbbbbbbb
cccccccc
</p><p>
dddddddd
eeeeeeee
</p>
<blockquote>
<p>
ffffffff
gggggggg
</p><p>
hhhhhhhh
<p>
</blockquote>
 
o qualche cosa di ragionevolmente simile? Ho cercato molto in Rete ma
non ho trovato nessun esempio.
-- 
Sandro kensan 
www.kensan.it
 & 
www.qiqi.it
 geek site


devi cercare "Espressioni regolari"
qui potresti trovarlo
http://regexlib.com/(X(1)A(o6gTL_HTnaioFQf4IScHsFP03yb5y9GY8AWUu_c2iqN___ERHCILuTTMgtjMsZYCzaQvaakYVFsDk023B-8WT4cAxtj4Q2BL5hx2qICetNf6RTmuVbBAfHE4vQxrLS1FpVUlPrhyV2KSqEQod7g1GGTQITtwQ9S5xIFBESAIyC25L3eKbQ0dXJsK468x7WZ_0))/?AspxAutoDetectCookieSupport=1
 
 
Leonardo
 
-- 
Non sono su facebook
Se anche tu vuoi cancellarti
http://www.giardiniblog.com/come-cancellarsi-da-facebook-eliminare-account/
 

intelligente da cambiare i break con i pragraph?
 
// A determined programmer can write a Perl program in any language.
 
$new = implode('',
	array_map(
		function($f) {
			return '' == trim($f)
				? ''
				: '<p>'.$f.'</p>';
		},
		preg_split('#<br\\s*/*>#', $text)
	)
);
 
Leonardo
 
-- 
 
Then felt I like some watcher of the skies, when a new planet swims into his ken;
Or like stout Cortez when with eagle eyes, he star'd at the Pacific; and all his men
Look'd at each other with a wild surmise - silent, upon a peak in Darien.

 Leonardo
 
Grazie della funzione ma io non cerco una espressione regolare ma un
"gestore" di DOM che mi permetta di inserire dei paragrafi <p> dove ci
sono dei break <br>. L'ho precisato anche nel testo quotato: "Usando DOM..."
 
In particolare ci sono moltissimi casi in cui una espressione fallisce.
Non so se sia il caso di questa, non l'ho testata, ma se viene inserito
un <p> prima del <blockquote></blockquote> e un </p> all'interno di un
<blockquote></blockquote> non va assolutamente bene e alcune espressioni
falliscono:
 
dddddddd
eeeeeeee
<blockquote>
ffffffff
gggggggg
<br><br>
hhhhhhhh
</blockquote>
 
===>>>
 
<p>
dddddddd
eeeeeeee
<blockquote>
ffffffff
gggggggg
</p><p>
hhhhhhhh
</blockquote>
</p>
 
Per questo volevo fosse usato l'albero DOM che è il modo corretto di
fare il parsing dell'HTML.


 fare il parsing dell'HTML.
 
Non puoi, usando il DOM, fare quello che chiedi, se non ricostruendo 
l'intero albero (o almeno il branch che contiene tutto il pezzo su cui 
stai lavorando).
 
Il modo più semplice è usando str_ireplace:
 
$res = '<p>'.str_ireplace(array('<br>','<br/>','<br />'),'</p><p>', 
$tuotesto).'</p>';
 
O con la regexp che ha suggerito Leonardo.
 
Ma hai due problemi:
 
1- non si gestisce mai l'HTML con le regexp: 
http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-
 
xhtml-self-contained-tags/1732454#1732454
 
2- HTML non è XML, quindi il DOM è un'opinione e non una certezza.
 
Io me lo terrei com'è. :D
 
Bye.

sono dei break <br>.
 
Ah, ho capito. La cosa e' un po' piu' difficile. Prova con...
 
    $doc    = new DOMDocument();
    $doc->loadHTML($text);
    for (;;) {
        $brs = $doc->getElementsByTagName('br');
        if (0 == $brs->length) {
            break;
        }
        foreach ($brs as $br) {
            $parent = $br->parentNode;
            foreach ([ $br->previousSibling, $br->nextSibling ] as $sib) {
                if ('#text' == $sib->nodeName) {
                    $parent->replaceChild(
                        $doc->createElement('p', $sib->nodeValue),
                        $sib);
                }
            }
            $parent->removeChild($br);
        }
    }
 
    // print $doc->saveHTML();


    }
 
M'ero scordato di aggiungere un dettaglio - questo approccio scala, rispetto
alla profondita' dell'albero DOM, piu' o meno come il barone Harkonnen con i
sospensori guasti.
 
Normalmente gli alberi sono roba umana e quindi tutto funziona. Ma questo in
condizioni, ripeto, normali.
 
Probabilmente sarebbe piu' efficiente un approccio bottom-up usando lambda a
stack, che scala bene finche' c'e' RAM (ed esplode se non c'e', ma mi sembra
che anche DOM abbia un discreto potere detonante). Si dovrebbe poter farlo a
colpi di XMLParser, anche se pero' a quel punto il documento DEVE essere ben
formato.
 
Rischio l'ira di Tony the Pony (he comes!) ma guasiguasi una bella regexp...


 Ah, ho capito. La cosa e' un po' piu' difficile. Prova con...
 
Era esattamente quello che volevo, grazie!
 
Ho modificato il codice in un punto perch� non mi funzionava ma rimane
un errore. Questo il codice:
 
<?php
    $text = file_get_contents('
http://www.kensan.it/tmp/Telnet.html'
);
 
    $doc    = new DOMDocument();
    $doc->loadHTML($text);
    for (;;) {
        $brs = $doc->getElementsByTagName('br');
        if (0 == $brs->length) {
            break;
        }
        foreach ($brs as $br) {
            $parent = $br->parentNode;
            $arr[]=$br->previousSibling;
            $arr[]=$br->nextSibling;
            foreach ( $arr as $sib) {
                if ('#text' == $sib->nodeName) {
                    $parent->replaceChild(
                        $doc->createElement('p', $sib->nodeValue),
                        $sib);
                }
            }
            $parent->removeChild($br);
        }
    }
 
    print $doc->saveHTML();
?>
 
E l'errore � questo:
 
Fatal error: Uncaught exception 'DOMException' with message 'Not Found
Error' in /home/tmp/paragraph.php:20 Stack trace: #0
/home/tmp/paragraph.php(20): DOMNode->replaceChild(Object(DOMElement),
Object(DOMText)) #1 {main} thrown in /home/tmp/paragraph.php on line 20
 
http://www.kensan.it/tmp/paragraph.php
 


Ho sbagliato io, il codice funziona perfettamente:
 
<?php
    $text = file_get_contents('
http://www.kensan.it/tmp/Telnet.html'
);
 
    $doc    = new DOMDocument();
    $doc->loadHTML($text);
    for (;;) {
        $brs = $doc->getElementsByTagName('br');
        if (0 == $brs->length) {
            break;
        }
        foreach ($brs as $br) {
            $parent = $br->parentNode;
            $Siblings=array($br->previousSibling, $br->nextSibling);
            foreach ( $Siblings as $sib) {
                if ('#text' == $sib->nodeName) {
                    $parent->replaceChild(
                        $doc->createElement('p', $sib->nodeValue),
                        $sib);
                }
            }
            $parent->removeChild($br);
        }
    }
 
    print $doc->saveHTML();
?>
 
Per� non fa proprio quello che dovrebbe:
 
<body> Telnet � il principale protocollo di Internet per realizzare
delle connessioni con macchine in remoto. Da la possibilit� di essere
fisicamente sul proprio sistema e lavorare su un altro che pu� essere
nella stanza accanto come dall'altra parte del mondo, la propria
macchina (locale) diventa cos� un terminale remoto: tecnicamente
l'operazione � definita <i>Remote Login</i>.<br><br>
 
***********
 
<body> Telnet � il principale protocollo di Internet per realizzare
delle connessioni con macchine in remoto. Da la possibilit� di essere
fisicamente sul proprio sistema e lavorare su un altro che pu� essere
nella stanza accanto come dall'altra parte del mondo, la propria
macchina (locale) diventa cos� un terminale remoto: tecnicamente
l'operazione � definita <i>Remote Login</i><p>.</p><p>
 
http://www.kensan.it/tmp/Telnet.html
 
http://www.kensan.it/tmp/paragraph.php
 


...<i>Remote Login</i><p>.</p>
 
In alternativa potremmo dire: "Subito prima del primo tag della sequenza di
pari livello", che in QUESTO caso darebbe il risultato probabilmente voluto
(non sono sicuro. Neanch'io implemento Telepathic DOM):
 
     <body><p>Telnet...<i>Remote Login</i>.</p>
 
Il problema e' che sono abbastanza sicuro che ci siano altri casi in cui il
criterio #2 da' il risultato sbagliato e l'#1 quello giusto.
 
Comunque, "basta" sostituire $br->previousSibling con $parent->firstSibling
e raccomandarsi all'Architetto.
 
Ma in un caso del genere, per sicurezza, ci vorrebbe un sistema che "scala"
ancora peggio, ovvero:
 
	- finche' ce n'e'
		- trovare il <br> piu' nidificato di tutti
		- eliminarlo


 criterio #2 da' il risultato sbagliato e l'#1 quello giusto.
 
Esatto, è proprio così.
 
Però forse il problema non è così complicato. Io ne so poco di DOM e di
alberi ma azzardo:
 
invece di comprendere solo il "." con $br->previousSibling nell'esempio
sopra, non è possibile comprendere anche il "ramo" <i>Remote Login</i>
perché è compreso in una white list che posso modificare che comprende i
tag:
 
<i>, <b>, <u> e pochi altri?
 
tag che non hanno nidificazioni all'interno ovvero non hanno child con
altri tag dentro con dei br.
 
Sono dei tag che fanno parte del testo e dovrebbero essere compresi in
$br->previousSibling con tutto il testo che precede fino al <begin>.
 
Che dici? Fa sempre parte del TDOM oppure ha controindicazioni questo
metodo basato su una white list?
 
Tieni conto si tratta di un html molto semplice.


Tieni conto si tratta di un html molto semplice.
 
E sara' bene: perche' mi sa che come operazione costi un botto :-)


copia & incolla a mano).
 
Oppure uno ci pensa du' menutini e lo fa nel modo giusto, ovvero camminando a
ritroso come 'e gamberi, e *sfruttando* il fatto che la lista e' eraclitea.
 
Come al solito garantisco freschezza (e poca pure di quella), non verita':
 
    $doc    = new DOMDocument();
    $doc->loadHTML($text);
    $brs = $doc->getElementsByTagName('br');
    while ($brs->length) {
        $br = $brs->item(0);
        $parent = $br->parentNode;
        foreach ([
            'nextSibling'       => function($p, $s) { $p->appendChild($s); },
            'previousSibling'   => function($p, $s) { $p->insertBefore($s,
$p->childNodes->item(0)); }
        ] as $direction => $smerlo) {
            $para   = $doc->createElement('p', '');
            for (;;) {
                if (null == ($sib = $br->$direction)) {
                    break;
                }
                if (!in_array($sib->nodeName, [ '#text', 'b', 'strong', 'i',
'em' ])) {
                    break;
                }
                //if ('#text' == $sib->nodeName) {
                //    if ('pre' != $sib->parentNode->nodeName) {
                //        $sib->nodeValue = preg_replace("#\\s+#", ' ',
$sib->nodeValue);
                //    }
                //}
                $smerlo($para, $sib);
            }
            if ($para->childNodes->length) {
                $parent->insertBefore($para, $br->nextSibling);
            }
        }
        $parent->removeChild($br);
    }
    print $doc->saveHTML();
 
Incidentalmente xmllint m'avverte che t'e' cascato un '</tt>' dove non doveva,
Ti potrebbe interessare 
http://htmlpurifier.org/
 . Oltre che xmllint.


Allora il programma � perfetto come sempre per� non mi piaceva in toto e
l'ho quindi modificato un po'. Non mi piace che metta ad ogni br un
paragrafo ma mi piace lo metta solo quando ci sono due br consecutivi.
Ho studiato un po' e sono un po' riuscito a capire questo DOM, questo �
il risultato funzionate (ma molto poco elegante). Probabilemtne si pu�
fare di meglio con due istruzioni in croce ma io sono un principiante...
 
$text = file_get_contents('
http://www.kensan.it/tmp/Telnet.html'
);
$doc    = new DOMDocument();
$doc->loadHTML($text);
 
// questa prima parte mette un tag di nome doublebr dove ci sono una
// coppia di br consecutive
 
$brs = $doc->getElementsByTagName('br');
foreach ($brs as $br) {
    $coppia=0;
    if ($br->previousSibling->nodeName == 'br') {$coppia=1;
$vicino=$br->previousSibling;}
    if ($br->nextSibling->nodeName == 'br') {$coppia=1;
$vicino=$br->nextSibling;}
    if ($coppia == 1) {
	$parent = $br->parentNode;
	$parent->replaceChild(
		    $doc->createElement('doublebr'),
		    $br);
	$parent->removeChild($vicino);
 
 
    }
 
  }
 
// segue il programma di Leonardo che insegue i doublebr invece dei br
// normali. i br normali sono, invece, inseriti nella white list
 
$brs = $doc->getElementsByTagName('doublebr');
while ($brs->length) {
    $br = $brs->item(0);
    $parent = $br->parentNode;
    foreach (array('nextSibling' => create_function('$p, $s', '
$p->appendChild($s); '), 'previousSibling'   => create_function('$p,
$s', ' $p->insertBefore($s,$p->childNodes->item(0)); ')) as $direction
=> $smerlo) {
	$para   = $doc->createElement('p', '');
	for (;;) {
	    if (null == ($sib = $br->$direction)) {
		break;
	    }
	    if (!in_array($sib->nodeName, array( '#text', 'b', 'strong', 'i',
'em', 'a', 'tt', 'span', 'br' ))) {
		break;
	    }
	    $smerlo($para, $sib);
	}
	if ($para->childNodes->length) {
	    $parent->insertBefore($para, $br->nextSibling);
	}
    }
    $parent->removeChild($br);
}
print $doc->saveHTML();
 
Ci sarebbe ancora un difetto da correggere ma � oltre le mie possibilit�
e quindi se non si pu� fare ricorrer� al metodo self-bangalore ovvero
faccio a manina.
 
Pare che se un tag non � chiuso il programma non funzioni. Ad esempio
 
uno due tre <br><br> quattro<tt>uno due<br><br>tre quattro</tt>
 
non vengono messi correttamente (??) i 3 <p> cio�:
 
<p>uno due tre </p><p> quattro<tt>uno due</p><p>tre quattro</tt></p>
 
Probabilmente perch� i tag vanno solo innestati l'uno sull'altro e non
sovrapposti parzialmente e quindi c'� un problema di fondo di un qualche
tipo. Sbaglio?
 
Comunque grazie del prezioso programma :)


<p>uno due tre </p><p> quattro<tt>uno due</p><p>tre quattro</tt></p>
 
No, il modo corretto dal punto di vista FSM e':
 
 <p>uno due tre</p><p>quattro<p><tt><p>uno due</p><p>tre quattro</p></tt>
 
e comunque non e' corretto perche' <tt> non puo' contenere <p>, mentre e'
possibile che contenga <br>.
 
Quindi bisognerebbe prima trasformare
 
	<tt>#text[1]<doublebr>#text[2]</tt>
 
in
	<p><tt>#text[1]</tt></p><p><tt>#text[2]</tt></p>
 
ovvero cercare tutti i tag TT e, di questi, quelli che contengono <br>^2.
