8768 sujets

Développement web côté serveur, CMS

Bonsoir
Je n'arrive pas à faire simplement l'opération suivante: supprimer par un preg_replace tous les <br> qui précèdent une balise </p>, par exemple
<br><br><br></p>
doit donner simplement </p>
Avez vous une solution autre qu'une boucle while ? Quan il n'y a qu'un seul caractère ou type de caractère pas de problème, mais une suite de caractères ...?
Modifié par PapyJP (19 Jun 2016 - 12:18)
Administrateur
Bonjour,

pour une répétition de chaînes de caractère, il existe les "quantifiers" *, ? et + :

[A-Z]*  =>  0 ou plus lettre majuscule
https?  ==>  un s ou pas de s, 0 ou 1 fois donc "http" et "https" seront [i]matchés[/i]
[0-9]+  ==>  1 chiffre 1 fois ou plus, correspond à un chiffre (0 à 9) ou un nombre (10, 999, 54321)

Résumé d'une dizaine de symboles de regexp

http://regexr.com/3dlbr : exemple compliqué permettant de repérer les <br> avec ou sans "/" (notation XHTML) avec ou sans whitespace (espace(s), tabulation(s) et retour(s) à la ligne) avant la balise fermante de p et entre les br
(<br\s*\/?>\s*)+(<\/p>)

Modificateurs : g pour plusieurs remplacements, m pour le multi-ligne et si tu as des balises en majuscules (ex : "<BR></P>"), également i pour "case Insensitive"


(<br>)+(<\/p>) ==> un ou plusieurs <br> avant </p> (en échappant le /)
(<br\/?>)+(<\/p>)  ==> avec un éventuel / de balise auto-fermante sauce XHTML
(<br\/?>\s*)+(<\/p>)  ==> et s il y avait des espaces (et des sauts de ligne !) entre les balises ?
(<br\s*\/?>\s*)+(<\/p>)  ==> et s il y avait des espaces entre "br" et ">" ou plus probablement entre "br" et "/>" càd "<br />"

Modifié par Felipe (19 Jun 2016 - 07:36)
Merci Felipe
Ce qui ne fonctionne pas, c'est le modifieur "g" qui fonctionne en JS mais pas en PHP.
J'ai fait une première série de preg_replace pour nettoyer certaines autres scories, ce qui fait que les \s*<br.*?> \* sont devenus de gentils <br>
J'ai actuellement un
preg_replace("#<br></p>#", "</p>", $text);
qui marche, mais au cas (malheureusement fort probable) où j'aurais <br><br>....</p>, j'aimerais pouvoir dire "supprimer TOUS les <br> qui précèdent telle ou telle balise sans faire une boucle while.
C'est d'autant plus important que </p> n'est pas la seule séquence de caractères à prendre en compte. J'ai même trouvé des <tr><br><td> totalement incongrus. Les navigateurs courants les ignorent, mais quand j'utilise un loadhtml en PHP il est totalement perdu.
D'où la nécessité de nettoyer la chaîne de caractères avant d'en faire un DOMDocument par la fonction loadhtml. Pour l'instant je n'ai pas trouvé de <tr><br><br>....<td> Mais je m'attends à tout!!
S'il n'y avait que quelques pages à prendre en compte, je les réécrirais en mettant le texte et les images par copier/coller depuis la page originelle, mais il y en a plusieurs centaines....
Administrateur
Pour "<br><br></p>", il faut chercher (<br>)+<\/p> équivalent à (<br>){1,}<\/p>: au moins un élément br (en échappant bien le slash)

Pour l'option g, en effet c'est en JS alors que preg_replace accepte un paramètre limit et peut renvoyer count (je ne fais plus que du jQuery et du JS ces temps-ci, plus de PHP Smiley smile )

Pour "<tr><br><td>", il existe une syntaxe pour reconnaître n'importe quelle balise HTML mais ce n'est pas forcément souhaitable ici s'il y a quelques occurences de "<br> collé à une balise" que tu voudrais vouloir garder.
L'astuce de départ est "tout sauf un chevron fermant >" : [^>]
Modifié par Felipe (19 Jun 2016 - 11:32)
Merci Felipe!
Ce que je cherchais c'est "comment rechercher un groupe de caractères répété".
Je suis coutumier de /\s+/ mais pas de (...)+
Pas clair dans la doc.
Bonjour !

Si vous permettez que je livre le résultat de mes cogitations :

$ligne2 = preg_replace('#(?:<br>)+(?=[</p>|</td>])#', '', $ligne1);
$ligne2 = preg_replace('#(?<=[<p>|<td>])(?:<br>)+#', '', $ligne2);


Bien sûr, c'est pour 'p' et 'td', mais on peut rajouter...

Par contre, comme je n'ai jamais rien compris avec ces histoires de greedy ou pas greedy...

Smiley smile

Edit : s'il y a une mise en forme du texte HTML, ça marche beaucoup moins bien. Smiley ohwell

Re-edit : laissez tomber pour ma solution on dirait qu'elle enlève tous les br sauf ce qui ceux à l'intérieur des textes... ce qui est a priori ce qui est demandé mais pour une raison que j'ignore...

Pour l'histoire de greedy, apparemment en PHP, les regex sont greedy par défaut. Pour le non-greedy, il faut préciser U après le motif : '#motif#U'...
Modifié par Zelena (19 Jun 2016 - 13:36)
Ça y est, j'ai trouvé :

$ligne2 = preg_replace('#(?:<br>)+(?=[<>])#', '', $ligne1);
$ligne2 = preg_replace('#(?<=[<>])(?:<br>)+#', '', $ligne2);


Le [... | ...| ...] ne permet pas des choix différents, juste de piocher entre différentes valeurs : il suffit donc de mettre [<>] pour se positionner devant ou derrière une balise HTML avec les lookahead et lookbehind positifs.

Pas de solution pour une mise en forme HTML.

Quel monologue !
Smiley lol
Zelena a écrit :
Pas de solution pour une mise en forme HTML.

Exact!

Dans mon programme, j'ai besoin d'avoir la structure toute nue d'un (très mauvais) HTML.
J'ai donc fait les opérations suivantes:
1) lire le texte depuis un fichier (sous forme de chaîne de caractères)
2) y appliquer des transformations "à la hache"
3) en faire un DOM par la fonction loadHTML

Voici ce que ça donne:

$htmlText = file_get_contents(... le fichier ...);
$htmlText = arrangeHTML($htmlText);
$errorReporting = error_reporting(E_ALL & ~E_WARNING); // pour ne pas avoir des foultitudes de warnings
$doc -> loadHTML($htmlText);
error_reporting($errorReporting);  //pour remettre le reporting d'erreur à sa valeur précédente
..................................
..................................
function arrangeHTML($hmlText) {
    $arrText = preg_replace_callback('#(</?\w+)#', 
        function($matches) {return   strtolower($matches[0]);}, $hmlText); //toutes les balises en minuscules
    $arrText = preg_replace('#</?tbody.*?>#', '', $arrText);//pas besoin de <tbody> dans mon contexte
    $arrText = preg_replace('#\s+#', ' ', $arrText); //supprimer les mises en forme HTML
    $arrText = preg_replace('#<p>\s*</p>#', '<br>', $arrText);//supprimer les <p> vides
    $arrText = preg_replace('#\s*<br.*?>\s*#', '<br>', $arrText);//<br/>  devient <br>
    $arrText = preg_replace('#(<br>)+(<t(d|h|r))#', '$2', $arrText);//supprimer les <br> en trop avant les balises <tr> <td> <th>
    $arrText = preg_replace('#(<t(r|d|h).*?>)(<br>)+#', '$1', $arrText);//idem après les mêmes balises
    $arrText = preg_replace('#(<br>)+(</(td|p))#', '$2', $arrText);//à la fin de certaines balises
    return $arrText;
}

Je m'attends à découvrir d'autres choses en poursuivant mon travail sur les centaines de pages accumulées depuis 2001...
Modifié par PapyJP (20 Jun 2016 - 11:13)