Forum Programmation.php Je suis nul en expression reguliere :(

Posté par (page perso) .
Tags : aucun
2
10
sept.
2008
Bonjour,

J'ai beau lire des tutoriaux sur les expressions reguliéres... et ben j'y arrive pas, je seche et ca me pose des problèmes...
j'ai 2 problèmes à resoudre mais je vais commencer par un dans l'espoir de resoudre le second tout seul comme un grand ;-)

J'extrait des données d'un champ texte, si le champ est inferieur a 575 caracteres j'affiche tout, sinon je n'affiche que les 250 premiers et je fais un lien sur une autre page :


if(strlen($row['texte']) < 575) {
$texte= explode("\n",$row['texte']);
} else { $texte = htmlentities(substr(html_entity_decode($row['texte'], ENT_QUOTES),0,249), ENT_QUOTES).' ...«a href="actualite.php?entree='.$row['id_agenda'].'"»Lire la suite«/a»';
$texte = explode("\n", $texte); }
foreach($texte as $value) {
echo '«p»'.bbCode($value).'«/p»'."\n"; }


Pour formater le texte l'utilisateur de la BD utilise des balises de type bbcode, qui sont transformées en balises html avec "ma" fonction bbCode (en partie honteusement pompée a divers endroit a cause des regexp... :)


function bbCode($t)
{

// faux titre h4
$t=str_replace("[titre]", "«span style=\"font-weight: bold; text-decoration:underline;\"»", $t);
$t=str_replace("[/titre]", "«/span»", $t);

// gras
$t=str_replace("[b]", "«span style=\"font-weight: bold;\"»", $t);
$t=str_replace("[/b]", "«/span»", $t);

// italique
$t=str_replace("[i]", "«span style=\"font-style: italic;\"»", $t);
$t=str_replace("[/i]", "", $t);

// soulignement
$t=str_replace("[u]", "«u»", $t);
$t=str_replace("[/u]", "«/u»", $t);

// couleur
$t=str_replace("[/color]", "", $t);
$regCouleur="\[color= ?((:alpha:+)|(#[[:digit:][:alpha:]]{6})) ?\]";
$t=ereg_replace($regCouleur, "«span style=\"color: \\1\"»", $t);

// lien
$regLienSimple="\[url\] ?([^\[]*) ?\[/url\]";
$regLienEtendu="\[url ?=([^\[]*) ?] ?([^]]*) ?\[/url\]";
if (ereg($regLienSimple, $t)) $t=ereg_replace($regLienSimple, "«a href=\"\\1\"»\\1", $t);
else $t=ereg_replace($regLienEtendu, "«a href=\"\\1\" target=\"_blank\"»\\2", $t);

// mail
$regMailSimple="\[email\] ?([^\[]*) ?\[/email\]";
$regMailEtendu="\[email ?=([^\[]*) ?] ?([^]]*) ?\[/email\]";
if (ereg($regMailSimple, $t)) $t=ereg_replace($regMailSimple, "«a href=\"mailto:\\1\"»\\1", $t);
else $t=ereg_replace($regMailEtendu, "«a href=\"mailto:\\1\"»\\2", $t);

// Bloc des balises [list]-[/list]
$t = preg_replace('`\[liste=(circle|disc|square|i)](.+?)\[/liste]`si','«ul»$2«/ul»',$t);
$t = preg_replace('`\[liste](.+?)\[/liste]`si','«ul style="margin:5px 10px 5px 15px;"»$1«/ul»',$t);

// Bloc des balises [*]
$t = preg_replace('`\[\*=(circle|disc|square|i)](.+?)`si','«li»$2',$t);
$t = preg_replace('`\[\*](.+?)\[/\*]`si','«li»$1',$t);
return $t;
}


Le problème est que si j'ai une balise bbcode non fermée dans les 250 premiers caractères, l'affichage part en vrille ou le code de la page n'est plus valide

comment pourrais-je détecter qu'il y a [xx] dans mon fragment de texte et qu'il n'y a pas [/xx] et que dans ce cas, je fais autre chose ?

Merci d'avance
++

PS: j'ai beau remplacer les "<" par des "«", j'ai une previsualisation un peu bizarre et des morceaux de code qui disparaisse ?
  • # les regexps ne resolvent pas tout

    Posté par (page perso) . Évalué à 2.

    il vous suffit de découper en token votre chaine, d'en faire un arbre, puis de dumper l'arbre en chaine.

    ca parait lourd comme ca mais c'est la meilleur solution pour restructurer votre code, et le cout algorithmique est quasi linéaire en fonction de la longueur de votre chaine.
    • [^] # Re: les regexps ne resolvent pas tout

      Posté par (page perso) . Évalué à 1.

      J'avoue ne pas comprendre... en fait si , je comprends, mais que j'utilise explode, split ou strtok, il me faut un delimiteur or , je peux avoir des balises imbriquées de type:

      blablabla [liste][*]element1[/*][*]element2[/*][*][b]element3[/b][/*][/liste] blablabla


      et là ca devient effectivement tres lourd, parcequ'il faudrait que je teste les balises une par une (genre avec un switch), renvoyé le delimiteur a strtok, recuperer la chaine litigieuse, la formater, et enfin reprendre le cours de l'affichage normal ... ouf (rien que de penser qu'il faut ecrire toutes ces lignes de code, j'ai subitement envie d'aller me coucher :D ) .

      Quoiqu'il en soit c'est effectivement une solution a laquelle je n'avais pas pensé, je vais m'y attarder, mais j'ai beaucoup d'entrée dans cette table, et j'ai un peu peur du cout en ressources (sur un serveur mutualisé qui plus est) ...

      Merci
      ++
      • [^] # Re: les regexps ne resolvent pas tout

        Posté par (page perso) . Évalué à 3.

        heu ... je ne sais pas comment tu te débrouilles ...

        tu alternes les recherches de [ et de ] ( ou tu split sur un motif qui ressemble à \[.*\] ) quand tu n'as pas de / le suivant est un fils, quand tu as un / le suivant est un frere du pere.

        apres tu parcours en profondeur d'abord ton arbre et tu ressors une chaine équilibrée.

        puis si tu ne veux pas te faire chier, tu stockes le résultat en BdD.

        ... mais ce que tu décris me fait peur à presque 3h du mat.
      • [^] # Commentaire supprimé

        Posté par . Évalué à 3.

        Ce commentaire a été supprimé par l'équipe de modération.

        • [^] # Re: les regexps ne resolvent pas tout

          Posté par (page perso) . Évalué à 4.

          - Tu utilises une pile et tu parcoures le texte d'entrée
          - Quand tu rencontres un tag ouvrant tu le push sur la pile
          - Quand tu rencontres un tag fermant tu le pop de la pile (en vérifiant que ce qu'il y a sur la pile correspond à ce que tu as en entrée
          - Après le nombre de caractère que tu souhaites est scanné ce qu'il reste sur la pile est ce qui n'a pas été fermé, donc, il suffite de faire poper un à un et de mettre le tag fermant correspondant dans le texte


          C'est l'esprit de ma solution ;)

          la seule différence repose sur sa nécessité de transformer du BBCode en HTML, passer par l'arbre permettra de gagner sur la seconde étape de parsing pour la traduction :)
  • # Quelque chose comme ...

    Posté par . Évalué à 2.


    if (preg_match('/\[title\](.*?)\[\/title\]/', $t, $result)) {
    $t = "<span style='font-weight: bold; text-decoration:underline;'>". $result[1] . "<’/span>";
    }

    De cette façon, il faut à la fois une balise ouvrante et une balise fermante pour qu'il y ait un remplacement.

    $result contient un résultat si la fonction preg_match réussie, avec $result[0] = la chaîne complète validant
    l'expression régulière et $result[1] = contenu de la chaîne entre les balises [titre].
    • [^] # Re: Quelque chose comme ...

      Posté par (page perso) . Évalué à 1.

      Un peu HS , mais faudra bien que j'y arrive un jour, c'est pas plus simple en utilisant une classe predifinie ([:alpha:] en l'occurence, mais je ne sais pas si * est dans la classe "alpha"...) , peux tu me dire si la syntaxe est bonne :

      if (preg_match('/\[[:alpha:]\](.*?)\[\/[:alpha:]\]/', $t, $result)) {
      echo $result[1];
      }
      // sauf qu'en cas de balise imbriquée ca marchera pas...

      revenons a nos oignons:
      c'est pas excatement ce que je cherche à faire :dans mon segment de 250 caracteres, je souhaite verifier
      1) la presence de [..]
      2 ) si presence il y a , [/..] est il present ou pas ? si oui on fait comme d'hab, si non, je nettoie les balises et j'affiche le texte sans formatage .
      Mon idée de depart etait celle là, mais faut que j'essaie aussi l'alternative proposée par Moun's
      ++
      • [^] # Re: Quelque chose comme ...

        Posté par . Évalué à 1.

        Je persiste avec les expressions régulières :

        function bb_decode($txt) {

        $title_reg = "/\[title\](.*?)\[\/title\]/";
        $title_rep = "<span style='font-weight: bold; text-decoration: underline;'>\\1</span>";

        $txt = preg_replace($title_reg, $title_rep, $txt);

        $bold_reg = "/\[b\](.*?)\[\/b\]/";
        $bold_rep = "<span style='font-weight: bold;'>\\1\</span>";

        $txt = preg_replace($bold_reg, $bold_rep, $txt);

        $underline_reg = "/\[u\](.*?)\[\/u\]/";
        $underline_rep = "<span style='text-decoration: underline;'>\\1</span>";

        $txt = preg_replace($underline_reg, $underline_rep, $txt);

        $italic_reg = "/\[i\](.*?)\[\/i\]/";
        $italic_rep = "<span style='font-style: italic;'>\\1</span>";

        $txt = preg_replace($italic_reg, $italic_rep, $txt);

        $email_reg = "/\[email\]([.\s]*?)\[\/email\]/";
        $email_rep = "<a href='\\1'>\\1</a>";

        $txt = preg_replace($email_reg, $email_rep, $txt);

        $url_reg = "/\[url\](.*?)\[\/url\]/";
        $url_rep = "<a href='\\1'>\\1</a>";

        $txt = preg_replace($url_reg, $url_rep, $txt);

        $color_rep = "<span style='color: \\1;'>\\2</span>";
        $color_reg = "/\[color=([a-z]+?|\#[0-9a-fA-F]{6})\](.*?)\[\/color\]/";

        $txt = preg_replace($color_reg, $color_rep, $txt);

        # nettoyage des balises incomplètes
        $balise_reg = "/\[\/?(title|b|u|i|email|url|color(=(.+?))?)\]/";

        $txt = preg_replace($balise_reg, "", $txt);

        return($txt);
        }

        Ça n'est pas complet, mais l'idée est là et ça semble fonctionner sur les quelques essais que j'ai fait.

  • # Backreference

    Posté par (page perso) . Évalué à 5.

    Perl permet les backreference et donc avoir une expression générique qui permet de matcher un début et la fin correspondante (attention aux backquote \, j'écris l'expression au format perl il faut ensuite adapter) :

    Donc on va matcher qu'une balise de début a bien une balise de fin correspondante (attention, on ne traite pas s'il y a des balises imbriquées :


    /\[(\w+)\].*?\[\/\1\]/


    Bien, maintenant on veut matcher toutes les balises, donc on les fait dans l'ordre avec la directive recommencer le match à la fin du dernier match (\G) :

    # encore en perl
    while( /\G\[(\w+)\](:?.*?\[\/\1\]?)/g )
    {
    # balise matchee dans $1
    # erreur si $2 vide
    }


    Et enfin on va gérer les balises imbriquées

    # toujours en perl
    sub valid
    {
    shift;
    while( /\G\[(\w+)\]((.*?)\[\/\1\]?)/g )
    {
    # balise matchee dans $1
    # erreur si $2 vide
    valid($3); #on recurse dans la sous partie
    }
    }


    Donc à adapter au php. J'espère ne pas avoir répondu trop a coté de la plaque. Au passage tu peux directement traiter tes balises dans la boucle while ...
    • [^] # Re: Backreference

      Posté par (page perso) . Évalué à 2.

      Je me répond pour me corriger :
      - Dans le 2e code il y a un ":?" qui n'a rien a faire là
      - Dans le 3e code, la regex doit se lire /\G.*?\[(\w+)\]((.*?)\[\/\1\])?/g
      - Et enfin on ne gère pas les balises imbriquées portant le même nom, ex : [a][a]toto[/a][/a]

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.