Les bases du formatage de textes

 

Dans cette partie, nous allons traiter les bases du formatage de textes. Dans une première partie, nous verrons comment rechercher des chaînes de caractères fixes grâce à la commande grep, puis nous aborderons la question centrale des expressions régulières. Enfin, nous utiliserons des expressions régulières pour faire des recherches au moyen de grep et de egrep.


Rechercher des chaînes de caractères


Dans cette partie, nous allons donner un aperçu de la recherche de chaînes de caractères fixes dans un texte. Comme nous l'avons déjà vu dans notre introduction au shell, la commande grep permet de rechercher un mot ou une phrase dans un ensemble de fichiers et renvoit toutes les lignes contenant la chaîne.

Pour chercher une chaîne (disons "new") dans un fichier (disons /usr/share/dict/words), on utilisera :

bash$ grep 'new' /usr/share/dict/words
Agnew
anew
knew
new
newborn
newcomer
newcomers
newer
newest
newly
newlywed
newness
newscast
newsgroup
newsletter
newsletters
newsman
newsmen
newspaper
newspapers
newsstand
newt
renew
renewable
renewal
renewed
renewer
renewing
renews
sinew
sinews
sinewy

On notera également que l'on peut passer à la commande grep une liste de fichiers, par exemple :

bash$ grep 'new' /usr/share/dict/words /usr/share/dict/words2
bash$ grep 'new' /usr/share/dict/words/*

Maintenant, si l'on recherche la chaîne "New" (et non plus "new"), on obtient :

bash$ grep 'New' /usr/share/dict/words
Newark
Newbold
Newbury
Newburyport
Newcastle
Newell
Newfoundland
Newman
Newmanize
Newmanizes
Newport
Newsweek
Newsweekly
Newton
Newtonian

La commande grep fait donc une différence entre les majuscules et les minuscules, on dit qu'elle est "case sensitive".

L'option -i (pour insensitive) a précisément pour but de dire à grep de ne pas distinguer majuscules et minuscules :

bash$ grep -i 'new' /usr/share/dict/words
Agnew
anew
knew
new
Newark
Newbold
newborn
Newbury
Newburyport
Newcastle
newcomer
newcomers
Newell
newer
newest
Newfoundland
newly
newlywed
Newman
Newmanize
Newmanizes
newness
Newport
NeWS
newscast
newsgroup
newsletter
newsletters
newsman
newsmen
newspaper
newspapers
newsstand
Newsweek
Newsweekly
newt
Newton
Newtonian
renew
renewable
renewal
renewed
renewer
renewing
renews
sinew
sinews
sinewy

Par ailleurs, il est parfois souhaitable de rechercher un mot exact (et pas seulement un mot contenant une certaine chaîne de caractères).

L'option -w (pour word) permet de dire à grep de ne rechercher que les mots correspondant à une chaîne donnée :

bash$ grep -w 'new' /usr/share/dict/words
new

De manière à retrouver plus facilement les occurrences de la chaîne de caractères, il est parfois souhaitable que grep indique le numéro de ligne de chacune de ses sorties.

L'option -n (pour number) permet de dire à grep de faire figurer le numéro de ligne devant ses réponses :

bash$ grep -n 'news' /usr/share/dict/words
27709:newscast
27710:newsgroup
27711:newsletter
27712:newsletters
27713:newsman
27714:newsmen
27715:newspaper
27716:newspapers
27717:newsstand
34196:renews
37481:sinews
bash$ grep -ni 'news' /usr/share/dict/words
27708:NeWS
27709:newscast
27710:newsgroup
27711:newsletter
27712:newsletters
27713:newsman
27714:newsmen
27715:newspaper
27716:newspapers
27717:newsstand
27718:Newsweek
27719:Newsweekly
34196:renews
37481:sinews

Parfois, on ne souhaite pas savoir où se trouvent les occurences d'une chaîne de caractères dans un fichier, mais seulement leur nombre.

L'option -c (pour count) permet de dire à grep de compter les occurrences d'une chaîne de caractères :

bash$ grep -c 'new' /usr/share/dict/words
32
bash$ grep -ci 'new' /usr/share/dict/words
48

Enfin, on souhaite parfois non pas rechercher mais éviter une chaîne de caractères.

L'option -v (pour avoid) permet de séléctionner (ou de compter) les lignes qui ne contiennent pas une chaîne de caractères :

bash$ grep -cvi 'e' /usr/share/dict/words
15149

Dans cet exemple, on compte le nombre de lignes de /usr/share/dict/words ne contenant pas les lettre 'e' et 'E'.

Exercice 4


Rechercher des expressions régulières


Premiers pas.

Dans la partie précédent, nous avons vu comment rechercher une chaîne de caractères dans un fichier ou un ensemble de fichiers. Ceci-dit, rechercher des chaînes de caractères ne suffit pas toujours. On peut être amenés à rechercher certains "motifs" (pattern en anglais) :

Aucun de ces cas ne peut être traité en se contentant de rechercher des chaînes de caractères. Pour mener à bien une telle missions, nous aurons besoin de la notion d'expression régulière.

L'expression régulière la plus simple que l'on puisse former est celle qui décrit un caractères pris parmi une certaine liste (on donne pour cela une liste de caractères entre [ et ]). Par exemple :

bash$ grep 'Ne[Ww]' /usr/share/dict/words
Newark
Newbold
Newbury
Newburyport
Newcastle
Newell
Newfoundland
Newman
Newmanize
Newmanizes
Newport
NeWS
Newsweek
Newsweekly
Newton
Newtonian

Il est alors primordial de comprendre le rôle des ' ' entourant l'expression à rechercher :

bash$ echo [tT]est
test
bash$ echo '[tT]est'
[tT]est

Les ' ' sont une sorte de "protection contre le shell" : ils l'empêchent d'interpréter ses arguments et sont indispensables.

Le nombre de caractères compris dans une liste n'est bien sûr pas limité à deux :

bash$ grep -w 's[aiueo]me' /usr/share/dict/words
same
some

Il est par ailleurs possible de spécifier un intervalle de caractères plutôt qu'une liste :

bash$ grep '[0-9]' /usr/share/dict/words
Modula-2
Modula-3
bash$ grep -w '[a-zA-Z]ew' /usr/share/dict/words
dew
few
hew
Jew
Lew
mew
new
pew
sew

Remarque : si l'on souhaite rechercher le caractère "-" lui-même, il suffit de le placer en premier dans la liste (dans ce cas il ne peut pas désigner un intervalle) :

bash$ grep '[-aiueo]brew' /usr/share/dict/words
Hebrew
Hebrews
home-brew
Killebrew

Il est par ailleurs possible de spécifier des expression négative, c'est-à-dire, de parler des caractères qui ne font pas partie d'une certaine liste. Par exemple, les caractères qui ne sont pas égaux à "p" seront désignés par "[^p]" :

bash$ grep 'a[^p]ple' /usr/share/dict/words
ample
counterexample
counterexamples
example
examples
sample
sampled
sampler
samplers
samples
trample
trampled
trampler
tramples
bash$ grep -w '[a-zA-Z][a-zA-Z]a[^p]ple' /usr/share/dict/words
example
trample

Le constructeur "^" peut également s'appliquer à un intervalle :

bash$ grep '[^a-zA-Z]' /usr/share/dict/words
Bhagavad-Gita
home-brew
Ibero-
L'vov
Modula-2
Modula-3
O'Brien
O'Connell
O'Connor
O'Dell
O'Donnell
O'Dwyer
O'Hare
O'Leary
O'Neill
O'Shea
O'Sullivan
Serbo-
Sino-
Uruguay'a

L'expression régulière "[^a-zA-Z]" désignant tout caractère non alphabétique.

On notera également que le "joker" '.' désigne n'importe quel caractère hormis le retour à la ligne.

Choisir un nombre de caractères.

Comme on l'a vu dans l'exemple de "[a-zA-Z][a-zA-Z]a[^p]ple", rechercher deux occurrences d'un certain caractère est un peu fastidieux ... et rechercher six occurrences de ce même caractère devient carrément insupportable !

Pour parler de n occurrences, on placera "\{n\}" juste après l'expression régulière. Par exemple, pour rechercher tous les mots contenant 4 voyelles successives, on utilisera l'expression suivante :

bash$ grep '[aiueo]\{4\}' /usr/share/dict/words
aqueous
dequeue
dequeued
dequeues
dequeuing
enqueue
enqueued
enqueues
Hawaiian
obsequious
pharmacopoeia
queue
queued
queueing
queuer
queuers
queues
queuing
Sequoia

Si l'on souhaite trouver tous les mots comportant une consonne suivie de 4 ou 5 voyelles puis d'un "s", on utilisera la variante :

bash$ grep '[^aiueoy][aiueo]\{4,5\}s' /usr/share/dict/words
aqueous
dequeues
enqueues
obsequious
queues

Enfin, si l'on souhaitre parler d'au moins 4 voyelles, on utilisera :

bash$ grep '[^aiueoy][aiueo]\{4,\}s' /usr/share/dict/words
aqueous
dequeues
enqueues
obsequious
queues

De même, pour caractériser au plus 4 voyelles, on utilisera :

'[aiueo]\{,4\}'

Terminons cette partie par deux constructions pratiques : un certain nombre de voyelles : '[aiueo]*'

bash$ grep 'asc[a-z]*ism' /usr/share/dict/words
asceticism
fascism

Définir le début et la fin d'une ligne.

Une caractéristique très pratique de grep est de pouvoir rechercher des lignes commençant et finissant par un certain motif. Ainsi, on pourra rechercher les lignes commençant par "lat" ou celles finissant par "ement" ...

Pour spécifier le début d'une ligne, on utilise le caractère "^" :

bash$ grep '^latt' /usr/share/dict/words
latter
latterly
lattice
lattices

Attention :
On prendra bien garde de ne pas confondre : "^[a-z]" et "[^a-z]" la première de ces expression désignant les lignes commençant par une minuscule et la seconde désignant les majuscules et les caractères spéciaux.

De même, on peut spécifier la fin d'une ligne en utilisant le caractère "$" :

bash$ grep 'pment$' /usr/share/dict/words
development
equipment
redevelopment
shipment

Ces deux caractères peuvent être combinés : l'expression régulière '^[yY][a-z]*y$' désigne toutes les lignes constituées d'un seul mot commençant par "y" ou "Y" et finissant par un "y".

Définir le début et la fin d'un mot.

De même que l'on peut spécifier le début et/ou la fin d'une ligne, on peut également spécifier le début et la fin d'un mot. Les ancres de début et de fin de mot sont respectivement '\<' et '\>'.

Ainsi pour rechercher les mots de /usr/share/dict/words terminant par 'pring' :

bash$ grep 'pring\>' /usr/share/dict/words
bedspring
offspring
spring

et les mots commençant par 'st' et terminant par 'ring' :

bash$ grep '\<st[a-zA-Z]*ring\>' /usr/share/dict/words
staggering
stammering
staring
starring
steering
stirring
storing
string
structuring

Enregistrer des occurrences.

La dernière possibilité des expressions régulières que nous allons aborder est celle de l'enregistrement puis du rappel d'occurrences d'un motif. Comme nous l'avons vu, l'expression '[aiueo][aiueo]' permet de recherche deux voyelles successives, mais comment rechercher deux voyelles successives identiques ? Il faut pour cela être capable de mémoriser la première voyelle, et de tester si la second est identique.

En utilisant les délimiteurs '\( ... \)', on caractérisera un motif qui pourra être rappelé par la suite. Par exemple, pour rechercher les mots comportant deux occurrences successives de 'x', 'y' ou 'z', on utilisera :

bash$ grep '\([xwy]\)\1' /usr/share/dict/words
Exxon

On notera que l'occurrence de 'x', 'y' ou 'z' est rappelée grâce à '\1'. De même :

bash$ grep '\([bcd]\)\([aiueo]\)\1\2' /usr/share/dict/words
Ababa
bobolink
bobolinks
coco
cocoa
coconut
coconuts
cocoon
cocoons
cucumber
cucumbers
Mbabane

Une commande peut utiliser jusqu'à 9 registres.

Rechercher des caractères spéciaux.

Comme nous l'avons vu, certains caractères (comme '[', '.' ou '\') ont un statut spécial parmi les expressions régulières. Comment, dès-lors, rechercher le caractère '[' dans un texte ? La solution va être de protéger ces caractères en les précédant d'un symbole '\' :

Par exemple :

bash$ echo -e 'the caracter \( is hard\nto find'
the caracter \( is hard
to find
bash$ echo -e 'the caracter \( is hard\nto find' | grep '\('
grep: Unmatched ( or \(
bash$ echo -e 'the caracter \( is hard\nto find' | grep '\\('
the caracter \( is hard

Exercice 5

Exercice 6