Précédent   Forum des professionnels en informatique > PHP > Langage > Regex
Regex Forum d'entraide sur les expressions rationnelles PHP. Avant de poster -> FAQ regex, Cours de regex et Sources de regex
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 06/05/2011, 10h25   #1
Membre confirmé
 
Inscription : septembre 2005
Messages : 724
Détails du profil
Informations forums :
Inscription : septembre 2005
Messages : 724
Points : 267
Points : 267
Par défaut Remplacement d'abbreviations par leurs définitions

Bonjour à tous,

Je souhaiterais coder une fonction PHP qui permettrait, dans une chaine HTML, de suffixer les abbreviations par leur définition placée entre parenthèses lors de la première occurrence trouvée, puis d'encapsuler les autres occurrences dans une balise <abbr>.

Ce qui donnerait comme exemple si le glossaire contient la définition de l'abbreviation PHP :

En entrée ce HTML :
Code :
<p>Le langage PHP est super <a href="#" title="PHP"> découvrir PHP</a> car PHP c'est top.</p>
Et en sortie :
Code :
<p>Le langage PHP (Hypertext Preprocessor) est super <a href="#" title="PHP"> découvrir <abbr title='Hypertext Preprocessor'>PHP</abbr></a> car <abbr title='Hypertext Preprocessor'>PHP</abbr> c'est top.</p>
Pour moi la plus grosse difficulté est de réussir à traiter les abbreviations seulement si elles sont placées dans un paragraphe, une liste à puce, un titre, etc. Il ne faudrait bien sur pas que la fonction aille bêtement tout remplacer car on se retrouvait avec des <abbr> dans les balises title ou alt...

L'autre difficulté mais qui est moindre, est de traiter différemment la première occurrence de l'abbreviation par rapport aux suivantes.

Enfin, concernant le glossaire, il s'agira d'un tableau PHP avec les abbreviations en clé et les définitions en valeur :

Code :
$glossaire = array('PHP'=>'Hypertext Preprocessor', 'ABC'=>'Autre définition');
Voilà tout, donc je suis preneur de conseils pour démarrer car j'étais parti sur du preg_replace_callback mais je me rend compte que je n'arrive pas à gérer différemment la première occurrence des suivantes car dans la fonction de callback nous ne pouvons pas savoir quelle est l'itération courante...

Merci d'avance !
vallica est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 06/05/2011, 12h24   #2
Modérateur
 
Avatar de Benjamin Delespierre
 
Benjamin Delespierre
Développeur Web
Inscription : février 2010
Messages : 2 984
Détails du profil
Informations personnelles :
Nom : Benjamin Delespierre
Âge : 24
Localisation : France

Informations professionnelles :
Activité : Développeur Web
Secteur : High Tech - Opérateur de télécommunications

Informations forums :
Inscription : février 2010
Messages : 2 984
Points : 5 016
Points : 5 016
Hello

Pas besoin de regexp pour ça, str_replace le fait très bien, la preuve:
Code :
1
2
3
4
5
6
7
8
9
10
11
 
$str = "PHP tourne la plupart du temps sur une stack LAMP";
$abbr = array(
  'PHP' => 'PHP (Hypertext PreProcessor)',
  'LAMP' => 'LAMP (Linux Apache MySQL PHP)',
);
 
echo str_replace(
  array_keys($abbr),
  array_values($abbr),
  $str);
__________________
A la recherche d'un framework MVC facile a prendre en main ? Essayez Axiom
Nouveau: la référence d'Axiom est disponible sur GitHub (je la peaufine en ce moment même).

Un problème correctement identifié est à moitié résolu, évitez de poster l'intégralité de votre code avec pour seule explication "ça ne marche pas...".
Pour identifier correctement vos problèmes PHP, utilisez la gestion des erreurs et xdebug.

Les boutons et existent, servez-vous en
Benjamin Delespierre est déconnecté   Envoyer un message privé Réponse avec citation 01
Vieux 06/05/2011, 12h28   #3
Membre Expert
 
Avatar de Djakisback
 
Inscription : février 2005
Messages : 1 791
Détails du profil
Informations forums :
Inscription : février 2005
Messages : 1 791
Points : 1 681
Points : 1 681
Utilise plutôt strtr() ça évitera de créer deux traitements et tableaux temporaires inutiles...

[EDIT]Hmm, effectivement, ça n'a rien à voir avec le problème posé, désolé...[EDIT]
Djakisback est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 06/05/2011, 12h34   #4
Modérateur
 
Avatar de Benjamin Delespierre
 
Benjamin Delespierre
Développeur Web
Inscription : février 2010
Messages : 2 984
Détails du profil
Informations personnelles :
Nom : Benjamin Delespierre
Âge : 24
Localisation : France

Informations professionnelles :
Activité : Développeur Web
Secteur : High Tech - Opérateur de télécommunications

Informations forums :
Inscription : février 2010
Messages : 2 984
Points : 5 016
Points : 5 016
Désolé j'avais mal lu ton post, effectivement c'est un poil plus complexe.

Voici comment tu peux procéder avec un closure:
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
$str = "PHP tourne sur une stack LAMP, PHP c'est le bien !";
 
$patterns = array('/PHP/', '/LAMP/');
$abbr = array('PHP' => 'Hypertext PreProcessor', 'LAMP' => 'Linux Apache MySQL PHP');
 
foreach ($patterns as $pattern) {
  $c = 0;
  $str = preg_replace_callback($pattern, function ($matches) use ($abbr, &$c) {
    $match = array_shift($matches);
    return ($c++ == 0) ? "$match ({$abbr[$match]})" : "<abbr title=\"{$abbr[$match]}\">$match</abbr>";
  }, $str);
}
 
echo $str;
Attention: cet exemple nécéssite PHP 5.3. Si tu veux le faire marcher sur PHP 5.2 ou inférieur, il va te faloir bricoler quelque chose avec les variables globales pour faire passer le compteur dans la callback puisque tu ne peux utiliser ni les closures ni le mot clé use.
__________________
A la recherche d'un framework MVC facile a prendre en main ? Essayez Axiom
Nouveau: la référence d'Axiom est disponible sur GitHub (je la peaufine en ce moment même).

Un problème correctement identifié est à moitié résolu, évitez de poster l'intégralité de votre code avec pour seule explication "ça ne marche pas...".
Pour identifier correctement vos problèmes PHP, utilisez la gestion des erreurs et xdebug.

Les boutons et existent, servez-vous en
Benjamin Delespierre est déconnecté   Envoyer un message privé Réponse avec citation 01
Vieux 06/05/2011, 13h01   #5
Membre confirmé
 
Inscription : septembre 2005
Messages : 724
Détails du profil
Informations forums :
Inscription : septembre 2005
Messages : 724
Points : 267
Points : 267
Merci Benjamin pour ce beau bout de code, seulement comme je le disais en début de topic, avec ce genre de code on va se retrouver avec des abbr dans les title et les alt...
__________________
eZ Publish certified developer
http://www.webaxis.fr
vallica est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 06/05/2011, 14h49   #6
Membre Expert
 
Avatar de Djakisback
 
Inscription : février 2005
Messages : 1 791
Détails du profil
Informations forums :
Inscription : février 2005
Messages : 1 791
Points : 1 681
Points : 1 681
Voilà un premier jet avec assertions :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$str = '<p>Le langage PHP est super <a href="#" title="PHP"> découvrir PHP</a> car PHP c\'est top.</p>
    <p>Le langage ABC est nul <a href="#" title="ABC"> ne pas découvrir ABC</a> car ABC c\'est nul.</p>';
 
$glossaire = array('PHP'=>'Hypertext Preprocessor', 'ABC'=>'Autre définition');
 
function callback($matches) {
    global $glossaire;
    if(isset($glossaire[$matches[0]]))    {
        $str = $glossaire[$matches[0]];
        unset($glossaire[$matches[0]]);
        return $matches[0].' ('.$str.')';
    }
    else    {
        return '<abbr title="'.$matches[0].'">'.$matches[0].'</abbr>';
    }
}
 
var_dump(preg_replace_callback('`(?!<[^<]*?)('.implode('|', array_keys($glossaire)).')(?![^<]*?>)`', 'callback', $str));
A tester...
Djakisback est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 06/05/2011, 15h23   #7
Membre confirmé
 
Inscription : septembre 2005
Messages : 724
Détails du profil
Informations forums :
Inscription : septembre 2005
Messages : 724
Points : 267
Points : 267
Enorme merci !

J'ai juste modifié un peu pour coller au contexte, c'est à dire que le title de la balise abbr doit contenir la definition du mot.

Donc ça donne :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public function add_abbr($string)
	{	
 
		global $glossaire; 
		$glossaire = array('PHP'=>array("Hypertext Preprocessor", false));			
 
		function callback($matches) {
 
			global $glossaire; 
		    if(!$glossaire[$matches[0]][1])    {
		        $str = $glossaire[$matches[0]][0];
		        $glossaire[$matches[0]][1]=true;
		        return $matches[0].' ('.$str.')';
		    }
		    else    {
		        return '<abbr title="'.$glossaire[$matches[0]][0].'">'.$matches[0].'</abbr>';
		    }
		}
 
		return preg_replace_callback('`(?!<[^<]*?)('.implode('|', array_keys($glossaire)).')(?![^<]*?>)`', 'callback', $string);
	}
J'ai du mal avec les regex, pourrais-tu m'expliquer vite fait ce que fait concretement la regex que tu passes à preg_replace_callback ?

J'ai compris la partie implode('|'....) qui consiste à capturer tous les termes que l'on veut remplacer par des abbreviations, mais ce que tu as mis avant et après ça signifie quoi exactement ?

Merci encore en tout cas, j'ai gagné un temps fou grâce à ton aide !
__________________
eZ Publish certified developer
http://www.webaxis.fr
vallica est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 06/05/2011, 19h23   #8
Membre Expert
 
Avatar de Djakisback
 
Inscription : février 2005
Messages : 1 791
Détails du profil
Informations forums :
Inscription : février 2005
Messages : 1 791
Points : 1 681
Points : 1 681
A la base c'est un vieux pattern que j'ai repris et modifié que j'avais chopé je ne sais où mais je m'aperçois que le pattern ne réagit pas tout à fait comme je le pensais.

Code :
'`(?!<[^<]*?)(PHP|ABC)`'
est censé matcher, je crois, PHP ou ABC non-précédé de '<' qui est suivi de n'importe quel caractère différent de '<' (en mode ungreedy avec le *?) mais normalement ça devrait être une assertion arrière et non pas une assertion avant et qui me semble inutile car le HTML est censé être bien formé.

Et il faut ajouter un délimiteur de mot (\b) pour ne pas remplacer par exemple 'PHPTRUC' ou autre.

Au final je pencherais plutôt pour ceci :
Code :
'`\b(PHP|ABC)\b(?![^<]*?>)`'
qui matche les mots PHP et ABC s'ils sont suivis de rien ou de la séquence 'XXXX<' mais pas 'XXXX>'.

Enfin, je t'avoues que c'est pas super clair non plus dans ma tête

(Je me demande si c'est exprimable avec une assertion positive avant, si quelqu'un passe dans le coin )
Djakisback est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 06/05/2011, 19h30   #9
Membre confirmé
 
Inscription : septembre 2005
Messages : 724
Détails du profil
Informations forums :
Inscription : septembre 2005
Messages : 724
Points : 267
Points : 267
Citation:
car le HTML est censé être bien formé
Effectivement, le HTML que je compte parser sera bien formé car passé par un validateur syntaxique auparavant.

J'essaye ton second jet de regex demain matin
__________________
eZ Publish certified developer
http://www.webaxis.fr
vallica est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 10/05/2011, 15h15   #10
Membre confirmé
 
Inscription : septembre 2005
Messages : 724
Détails du profil
Informations forums :
Inscription : septembre 2005
Messages : 724
Points : 267
Points : 267
Citation:
Au final je pencherais plutôt pour ceci :
Code :Sélectionner tout - Visualiser dans une fenêtre à part
'`\b(PHP|ABC)\b(?![^<]*?>)`'
qui matche les mots PHP et ABC s'ils sont suivis de rien ou de la séquence 'XXXX<' mais pas 'XXXX>'.

Enfin, je t'avoues que c'est pas super clair non plus dans ma tête
Après essai, ca provoque une fatal error au niveau de PHP... mon piètre niveau en Regex ne me permet pas de determiner pourquoi...

Mais sinon la première que tu m'a donné fonctionne plutôt bien on dirait, pourquoi la trouvais-tu mauvaise ?
__________________
eZ Publish certified developer
http://www.webaxis.fr
vallica est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 10/05/2011, 16h10   #11
Modérateur
 
Avatar de Nesmontou
 
Homme Benjamin PREVOT
Architecte de système d'information
Inscription : septembre 2004
Messages : 1 568
Détails du profil
Informations personnelles :
Nom : Homme Benjamin PREVOT
Âge : 30
Localisation : France, Nord (Nord Pas de Calais)

Informations professionnelles :
Activité : Architecte de système d'information
Secteur : Finance

Informations forums :
Inscription : septembre 2004
Messages : 1 568
Points : 2 493
Points : 2 493
Bonjour,

Ce code semble répondre à ta demande
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$chaine = '<p>Le langage PHP est super <a href="#" title="PHP"> découvrir PHP</a> car PHP c\'est top.</p><p>Le langage ABC est nul <a href="#" title="ABC"> ne pas découvrir ABC</a> car ABC c\'est nul.</p>';
 
$glossaire = array(
	'PHP' => 'Hypertext Preprocessor',
	'ABC' => 'Autre définition'
);
 
foreach ($glossaire as $key => $value) {
	$chaine = preg_replace('/((^|>)[^<]*)(' . $key . ')([^>]*(<|$))/', '$1<abbr title="' . $value . '">$3</abbr>$4', $chaine);
}
 
echo $chaine;
?>
Bon développement
__________________
Si vous ne pouvez expliquer un concept à un enfant de six ans, c'est que vous ne le comprenez pas complètement. Albert EINSTEIN

F.A.Q. : Java, PHP, (X)HTML / CSS

N'oubliez pas de cliquer sur le bouton Résolu en bas de page quand vous avez obtenu une solution à votre problème
Nesmontou est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 10/05/2011, 16h28   #12
Membre confirmé
 
Inscription : septembre 2005
Messages : 724
Détails du profil
Informations forums :
Inscription : septembre 2005
Messages : 724
Points : 267
Points : 267
Bonjour,

Merci pour ton aide mais en fait le besoin est de remplacer une première fois le terme par "terme (définition)" et d'encapsuler les occurrences suivantes dans une balise ABBR.

Ainsi lorsque tu lira le texte, la première occurrence de PHP sera expliquée entre parenthèse juste après le mot PHP, et toutes les autres occurrences de PHP dans la page seront encapsulées dans une balise <abbr>.
__________________
eZ Publish certified developer
http://www.webaxis.fr
vallica est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 10/05/2011, 17h13   #13
Modérateur
 
Avatar de Benjamin Delespierre
 
Benjamin Delespierre
Développeur Web
Inscription : février 2010
Messages : 2 984
Détails du profil
Informations personnelles :
Nom : Benjamin Delespierre
Âge : 24
Localisation : France

Informations professionnelles :
Activité : Développeur Web
Secteur : High Tech - Opérateur de télécommunications

Informations forums :
Inscription : février 2010
Messages : 2 984
Points : 5 016
Points : 5 016
A mon avis on va se creuser la soupière encore longtemps avec ton code... C'est vraiment tordu comme besoin.
__________________
A la recherche d'un framework MVC facile a prendre en main ? Essayez Axiom
Nouveau: la référence d'Axiom est disponible sur GitHub (je la peaufine en ce moment même).

Un problème correctement identifié est à moitié résolu, évitez de poster l'intégralité de votre code avec pour seule explication "ça ne marche pas...".
Pour identifier correctement vos problèmes PHP, utilisez la gestion des erreurs et xdebug.

Les boutons et existent, servez-vous en
Benjamin Delespierre est déconnecté   Envoyer un message privé Réponse avec citation 01
Vieux 10/05/2011, 17h17   #14
Membre confirmé
 
Inscription : septembre 2005
Messages : 724
Détails du profil
Informations forums :
Inscription : septembre 2005
Messages : 724
Points : 267
Points : 267
Tout à fait d'accord, c'est un besoin qui n'a pas été pensé pour le développeur

Bon mais pour résumer cette solution donnée plus haut semble fonctionner :

Code :
var_dump(preg_replace_callback('`(?!<[^<]*?)('.implode('|', array_keys($glossaire)).')(?![^<]*?>)`', 'callback', $str));
Sauf que nous ne sommes pas certains que cette regex fasse bien le boulot demandé c'est à dire capturer du texte uniquement entre les balises :

Code :
<a href="ICI JE CAPTURE PAS" alt="ICI NON PLUS">ICI JE CAPTURE</a>
__________________
eZ Publish certified developer
http://www.webaxis.fr
vallica est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 10/05/2011, 19h22   #15
Membre Expert
 
Avatar de Djakisback
 
Inscription : février 2005
Messages : 1 791
Détails du profil
Informations forums :
Inscription : février 2005
Messages : 1 791
Points : 1 681
Points : 1 681
Citation:
Envoyé par vallica Voir le message
Après essai, ca provoque une fatal error au niveau de PHP... mon piètre niveau en Regex ne me permet pas de determiner pourquoi...

Mais sinon la première que tu m'a donné fonctionne plutôt bien on dirait, pourquoi la trouvais-tu mauvaise ?
En fait je la trouvais mauvaise car 'PHP' est remplacé aussi dans des chaînes de caractères comme 'PHPTRUC', donc dans tous les cas il faudrait ajouter des délimiteurs de mot \b.
Mon autre interrogation était plutôt sur les perfs car la première assertion me semble inutile, à moins qu'elle ne joue sur les perfs en limitant la recherche mais là, je ne sais pas.

Bizarre que le 2e pattern plante, je l'avais testé. Est-ce que tu l'avais bien utilisé de cette manière :
Code :
1
2
 
var_dump(preg_replace_callback('`\b('.implode('|', array_keys($glossaire)).')\b(?![^<]*?>)`', 'callback', $str));
Djakisback est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 11/05/2011, 08h06   #16
Modérateur
 
Avatar de Nesmontou
 
Homme Benjamin PREVOT
Architecte de système d'information
Inscription : septembre 2004
Messages : 1 568
Détails du profil
Informations personnelles :
Nom : Homme Benjamin PREVOT
Âge : 30
Localisation : France, Nord (Nord Pas de Calais)

Informations professionnelles :
Activité : Architecte de système d'information
Secteur : Finance

Informations forums :
Inscription : septembre 2004
Messages : 1 568
Points : 2 493
Points : 2 493
Bonjour,

J'ai trouvé sympa ce petit problème, alors je me suis un peu attardé dessus

Voilà ce que je pensais faire
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
// Chaine à traiter
$chaine = '<p>Le langage PHP est super <a href="#" title="PHP"> découvrir PHP</a> car PHP c\'est top.</p><p>Le langage ABC est nul <a href="#" title="ABC"> ne pas découvrir ABC</a> car ABC c\'est nul.</p>';
 
// Liste des abréviations et définitions
$glossaire = array(
	'PHP' => 'Hypertext Preprocessor',
	'ABC' => 'Autre définition'
);
 
// Liste des abréviations traitées (initialement aucune)
$traitees = array();
 
// "Eclatement" de la chaine suivant les balises et les limites de mots (on capture les délimiteurs)
$split = preg_split('/(<[^>]+>|\b)/', $chaine, -1, PREG_SPLIT_DELIM_CAPTURE);
 
// Itération sur chaque élément
foreach ($split as $key => $value) {
	if (isset($glossaire[$value])) { // Si une définition existe
		$definition = $glossaire[$value];
 
		if (in_array($value, $traitees)) { // Si l'abréviation a déjà été traitée
			$split[$key] = '<abbr title="' . $definition . '">' . $value . '</abbr>';
		} else { // Si l'abréviation n'a pas encore été traitée
			$split[$key] = $value . ' (' . $definition . ')';
 
			$traitees[] = $value; // On enregistre l'abréviation comme traitée
		}
	}
}
 
echo implode('', $split); // Résultat
?>
J'espère que ça permettra d'avancer
__________________
Si vous ne pouvez expliquer un concept à un enfant de six ans, c'est que vous ne le comprenez pas complètement. Albert EINSTEIN

F.A.Q. : Java, PHP, (X)HTML / CSS

N'oubliez pas de cliquer sur le bouton Résolu en bas de page quand vous avez obtenu une solution à votre problème
Nesmontou est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/05/2011, 09h24   #17
Membre confirmé
 
Inscription : septembre 2005
Messages : 724
Détails du profil
Informations forums :
Inscription : septembre 2005
Messages : 724
Points : 267
Points : 267
Citation:
Envoyé par Nesmontou Voir le message
Bonjour,

J'ai trouvé sympa ce petit problème, alors je me suis un peu attardé dessus
C'est très gentil de ta part

Citation:
Envoyé par Nesmontou Voir le message
J'espère que ça permettra d'avancer
Merci ton code marche très bien et je découvre au passage l'existence ainsi que le fonctionnement de preg_split !

Un grand merci général à tous ceux qui m'ont aidé sur ce post, j'ai maintenant deux solutions qui marchent très bien, c'est grâce à vous !
__________________
eZ Publish certified developer
http://www.webaxis.fr
vallica est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité Cette discussion est résolue.
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 09h04.


 
 
 
 
Partenaires

Hébergement Web