Regexp

Fermé
Utilisateur anonyme - 28 févr. 2013 à 16:22
mamiemando Messages postés 33572 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 13 mars 2025 - 2 mars 2013 à 14:00
Bonjour,

je cherche l'expression qui me permet d'afficher les phrases d'un texte qui se terminent par un point d'interrogation ou d'exclamation.

Merci d'avance.

1 réponse

mamiemando Messages postés 33572 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 13 mars 2025 7 833
Modifié par mamiemando le 2/03/2013 à 14:00
En fait je ne pense pas qu'une regexp soit le moyen le plus adapté pour ce genre de problème. Une regexp cherche UN motif dans une chaîne, ici tu veux en extraire plusieurs, donc déjà ce n'est pas un très bon départ.Supposons que tu aies une phrases par ligne, cette commande ne va conserver que les lignes qui contiennent au moins un caractère suivi d'un "?" ou d'un "!" :

cat plop.txt | egrep ".+(\?|\!)"


Note qu'ici ? et ! sont deux opérateurs particuliers qui peuvent faire planter ma commande egrep, donc pour éviter les soucis et dire à egrep qu'il s'agit bien des deux caractères ? et ! et non des opérateurs, je dois rajouter un \ devant.

Maintenant regardons ce que peuvent faire des outils comme sed : tu peux par exemple substitué les chaîne qui ne t'intéressent pas, donc les phrases qui ne finissent pas par "!" et"?" par une chaîne vide.

Si plusieurs phrases sont sur la même ligne tu pourras appliquer cette opération grâce à l'option /g de sed (sed -i "s/motif/subst/g"). Si ton fichier comporte plusieurs lignes tu peux itérer sur chaque ligne grâce à un pipe, comme je viens de le faire avec egrep.

Pour plus de lisibilité, quand je trouve un motif qui m'intéresse (ici une phrase qui finit par ? ou !) je vais l'encadrer avec les caractère < >.

(mando@silk) (~) $ cat plop.txt 
Salut ! 
Salut. 
J'aime les tapirs. Et toi ? 

<code>(mando@silk) (~) $ cat plop.txt | sed -e "s/\([^.]*[?\!]\)/<\1>/g" 
<Salut !> 
Salut. 
J'aime les tapirs.< Et toi ?>


Explications sur la syntaxe de sed :
- s/ : substitution
- \([^.]*[?\!]\) : le motif que je veux détecter (que j'explique plus loin)
- / sépare le motif du substituant
- <\1> : \1 fait référence au premier bloc de mon motif encadré par les symboles \( et \), en l'occurrence [^.]*[?\!]. Donc là en gros, quand la substitution a lieu, écris < suivit du bloc de caractère auquel \1 fait référence, suivi de >
- /g : répète cette opération tant que tu trouves des motifs sur la chaîne que tu évalues (ici chaque ligne du fichier puisqu'on passe par un pipe).

Passons à l'expression régulière elle-même. On a vu que \( \) servait dans sed à récupérer tout ou une partie du motif. Ici je récupère tout le motif. Il ne reste plus qu'à regarder [^.]*[?\!].

Une phrase se finit par un ".", un "?" ou un "!". Encore une fois certains de ces caractères peuvent faire référence à des opérateurs donc j'ai dû mettre un \ devant certains d'entre eux dans mon expression rationnelle.

Donc une phrase qui m'intéresse est une suite d'au moins un caractère tels que chaque caractère n'est pas un "." suivi de "!" ou "?". Un caractère autre point s'écrit [^.]. Une suite d'au moins un élément se modélise avec l'opérateur +, donc ça donne [^.]+. Ensuite un caractère appartenant à la classe de caractère {?, !} peut s'écrire [?!], mais il existe d'autres syntaxes (genre (\!|\?) etc...).

Du coup si je trouve une suite d'au moins 1 caractère (autre que ".") suivi de ? ou !, alors j'ai trouvé une phrase qui m'intéresse, ce qui correspond bien à l'expression régulière [^.]*[?\!].

Ok maintenant il reste un dernier problème, c'est si la phrase est à cheval sur plusieurs lignes. Le problème du pipe, c'est que on écrit le fichier avec cat et à chaque fois qu'un passage à la ligne est détecté, cette chaîne (donc la ligne courante) est transmise à sed. Ainsi sed n'agit pas sur le texte dans sa globalité mais ligne par ligne.

Heureusement, on peut appliquer directement la commande sed sur un fichier. Par contre écrire [^.] ne prend pas en compte le caractère spécial fin de ligne, noté \n, donc il suffirait d'écrire [^.n]. Bon maintenant, revenons au problème : pour le moment on détecte les chaînes qui nous intéresse, changeons l'expression régulière pour substituer ce qui ne nous intéresse pas par du vide.

Si on adopte le même raisonnement que plus haut, ce qui ne nous intéresse pas est décrit par l'expression régulière [^\!?\n]*[.]. Pas besoin de récupérer ce qui a été rattraper par le motif puisque le substituant n'en dépend pas, donc pas besoin de \( \) ou de \1.

J'obitens alors :

(mando@silk) (~) $ cat plop  
Salut ! 
Salut. 
J'aime les tapirs. Et 
toi ? 

(mando@silk) (~) $ sed -e "s/[^\!?\n]*[.]//g" plop.txt 
Salut ! 

 Et 
toi ?


Ensuite on peut dégager les lignes vides. Le métacaractère qui désigne "début de ligne" s'écrit ^, tandis que fin de ligne est représenté par le métacaractère $. Ainsi "^$" désigne une chaîne vide (par opposition par exemple à "^toto$" qui désigne la chaîne exactement égale à "toto", tandis que "toto" désigne une ligne qui contient toto). Il suffit donc de filtrer ces lignes. Qui dit filtre dit grep. L'option -v va me permettre de ne conserver que ce qui ne correspond pas au motif que je passe en paramètre à grep, et c'est exactement comme ça que je vais filtrer les lignes vides :

(mando@silk) (~) $ sed -e "s/[^\!?\n]*[.]//g" plop | grep -v "^$" 
Salut ! 
 Et 
toi ?


Bonne chance
0