Licence CC 0

TP : l'en-tête <string.h>

Dernière mise à jour :

Les quatre derniers cours ayant été riche en nouveautés, posons nous un instant pour mettre en pratique tout cela.

Préparation

Dans le chapitre précédent, nous vous avons dit qu’une chaîne de caractères se manipulait comme un tableau, à savoir en parcourant ses éléments un à un. Cependant, si cela s’arrêtait là, cela serait assez gênant. En effet, la moindre opération sur une chaîne nécessiterait d’accéder à ses différents éléments, que ce soit une modification, une copie, une comparaison, etc. Heureusement pour nous, la bibliothèque standard fourni une suite de fonction nous permettant de réaliser plusieurs opérations de base sur des chaînes de caractères. Ces fonctions sont déclarées dans l’en-tête <string.h>.

L’objectif de ce TP sera de réaliser une version pour chacune des fonctions de cet en-tête qui vont vous êtes présentées. :)

strlen

1
size_t strlen(char *chaine);

La fonction strlen() vous permet de connaître la taille d’une chaîne fournie en argument. Celle-ci retourne une valeur de type size_t. Notez bien que la longueur retournée ne comprend pas le caractère nul. L’exemple ci-dessous affiche la taille de la chaîne « Bonjour ».

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <string.h>


int main(void)
{
    printf("Longueur : %u\n", (unsigned)strlen("Bonjour"));
    return 0;
}
1
Longueur : 7

strcpy

1
char *strcpy(char *destination, char *source);

La fonction strcpy() copie le contenu de la chaîne source dans la chaîne destination, caractère nul compris. La fonction retourne l’adresse de destination. L’exemple ci-dessous copie la chaîne « Au revoir » dans la chaîne chaine.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <stdio.h>


int main(void)
{
    char chaine[25] = "Bonjour\n";

    strcpy(chaine, "Au revoir");
    printf("%s\n", chaine);
    return 0;
}
1
Au revoir

La fonction strcpy() n’effectue aucune vérifications. Vous devez donc vous assurer que la chaîne de destination dispose de suffisamment d’espace pour accueillir la chaîne qui doit être copiée (caractère nul compris !).

strcat

1
char *strcat(char *destination, char *source);

La fonction strcat() ajoute le contenu de la chaine source à celui de la chaîne destination, caractère nul compris. La fonction retourne l’adresse de destination. L’exemple ci-dessous ajoute la chaîne « tout le monde » au contenu de la chaîne chaine.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <stdio.h>
#include <string.h>


int main(void)
{
    char chaine[25] = "Bonjour";

    strcat(chaine, " tout le monde");
    printf("%s\n", chaine);
    return 0;
}
1
Bonjour tout le monde

Comme strcpy(), la fonction strcat() n’effectue aucune vérifications. Vous devez donc vous assurer que la chaîne de destination dispose de suffisamment d’espace pour accueillir la chaîne qui doit être ajoutée (caractère nul compris !).

strcmp

1
int strcmp(char *chaine1, char *chaine2);

La fonction strcmp() compare deux chaines de caractères. Cette fonction retourne :

  • une valeur positive si la première chaîne est « plus grande » que la seconde ;
  • zéro si elles sont égales ;
  • une valeur négative si la seconde chaîne est « plus grande » que la première.

Ne vous inquiétez pas au sujet des valeurs positives et négatives, nous y reviendront en temps voulu lorsque nous aborderons les notions de jeux de caractères et d’encodages dans la troisième partie du cours. En attendant, effectuez simplement une comparaison entre les deux caractères qui diffèrent.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <stdio.h>
#include <string.h>


int main(void)
{
    char chaine1[] = "Bonjour";
    char chaine2[] = "Au revoir";

    if (strcmp(chaine1, chaine2) == 0)
        printf("Les deux chaînes sont identiques\n");
    else
        printf("Les deux chaînes sont différentes\n");

    return 0;
}
1
Les deux chaînes sont différentes

strchr

1
char *strchr(char *chaine, int ch);

La fonction strchr() recherche la présence du caractère ch dans la chaîne chaine. Si celui-ci est rencontré, la fonction retourne l’adresse de la première occurrence de celui-ci au sein de la chaîne. Dans le cas contraire, la fonction renvoie un pointeur nul.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <stddef.h>
#include <stdio.h>
#include <string.h>


int main(void)
{
    char chaine[] = "Bonjour";
    char *p;

    p = strchr(chaine, 'o');

    if (p != NULL)
        printf("La chaîne `%s' contient la lettre %c\n", chaine, *p);

    return 0;
}
1
La chaîne `Bonjour' contient la lettre o

strpbrk

1
char *strpbrk(char *chaine, char *liste);

La fonction strpbrk() recherche la présence d’un des caractères de la chaîne liste dans la chaîne chaine. Si un de ceux-ci est rencontré, la fonction retourne l’adresse de la première occurrence au sein de la chaîne. Dans le cas contraire, la fonction renvoie un pointeur nul.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <stddef.h>
#include <stdio.h>
#include <string.h>


int main(void)
{
    char chaine[] = "Bonjour";
    char *p;

    p = strpbrk(chaine, "aeiouy");

    if (p != NULL)
        printf("La première voyelle de la chaîne `%s' est : %c\n", chaine, *p);

    return 0;
}
1
La première voyelle de la chaîne `Bonjour' est : o

strstr

1
char *strstr(char *chaine1, char *chaine2);

La fonction strstr() recherche la présence de la chaîne chaine2 dans la chaîne chaine1. Si celle-ci est rencontrée, la fonction retourne l’adresse de la première occurrence de celle-ci au sein de la chaîne. Dans le cas contraire, la fonction renvoie un pointeur nul.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <stddef.h>
#include <stdio.h>
#include <string.h>


int main(void)
{
    char chaine[] = "Bonjour";
    char *p;

    p = strstr(chaine, "jour");

    if (p != NULL)
        printf("La chaîne `%s' contient la chaîne `%s'\n", chaine, p);

    return 0;
}
1
La chaîne `Bonjour' contient la chaîne `jour'

Ceci étant dit, à vous de jouer. ;)

Par convention, nous commencerons le nom de nos fonctions par la lettre « x » afin d’éviter des collisions avec ceux de l’en-tête <string.h>.

Correction

Bien, l’heure de la correction est venue. :pirate:

strlen

1
2
3
4
5
6
7
8
9
size_t xstrlen(char *chaine)
{
    size_t i;

    for (i = 0; chaine[i] != '\0'; ++i)
        ;

    return i;
}

strcpy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
char *xstrcpy(char *destination, char *source)
{
    size_t i;

    for (i = 0; source[i] != '\0'; ++i)
        destination[i] = source[i];

    destination[i] = '\0' /* N'oubliez pas le caractère nul final ! */
    return destination;
}

strcat

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
char *xstrcat(char *destination, char *source)
{
    size_t i;

    while (*destination != '\0')
        ++destination;

    for (i = 0; source[i] != '\0'; ++i)
        destination[i] = source[i];

    destination[i] = '\0'; /* N'oubliez pas le caractère nul final ! */
    return destination;
}

strcmp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int xstrcmp(char *chaine1, char *chaine2)
{
    while (*chaine1 == *chaine2)
    {
        if (*chaine1 == '\0')
            return 0;

        ++chaine1;
        ++chaine2;
    }

    return (*chaine1 < *chaine2) ? -1 : 1;
}

strchr

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
char *xstrchr(char *chaine, int ch)
{
    while (*chaine != '\0')
        if (*chaine == ch)
            return chaine;
        else
            ++chaine;

    return NULL;
}

strpbrk

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
char *xstrpbrk(char *chaine, char *liste)
{
    while (*chaine != '\0')
    {
        char *p = liste;

        while (*p != '\0')
        {
            if (*chaine == *p)
                return chaine;

            ++p;
        }

        ++chaine;
    }

    return NULL;
}

strstr

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
char *xstrstr(char *chaine1, char *chaine2)
{
    while (*chaine1 != '\0')
    {
        char *s = chaine1;
        char *t = chaine2;

        while (*s != '\0' && *t != '\0')
        {
            if (*s != *t)
                break;
      
            ++s;
            ++t;
        }

        if (*t == '\0')
            return chaine1;

        ++chaine1;
    }

    return NULL;
}

Pour allez plus loin : strtok

Pour les plus aventureux d’entre-vous, nous vous proposons de réaliser en plus la mise en œuvre de la fonction strtok() qui est un peu plus complexe que ses congénères. :)

1
char *strtok(char *chaine, char *liste);

La fonction strtok() est un peu particulière : elle divise la chaîne chaine en une suite de sous-chaînes délimitée par un des caractères présent dans la chaîne liste. En fait, cette dernière remplace les caractères de la chaîne liste par des caractères nuls dans la chaîne chaine (elle modifie donc la chaîne chaine !) et renvoie les différentes sous-chaînes ainsi créées au fur et à mesure de ses appels.

Lors des appels subséquents, la chaîne chaine doit être un pointeur nul afin de signaler à strtok() que nous souhaitons la sous-chaîne suivante. S’il n’y a plus de sous-chaîne ou qu’aucun caractère de la chaîne liste n’est trouvé, celle-ci retourne un pointeur nul.

L’exemple ci-dessous tente de scinder la chaîne « un, deux, trois » en trois sous-chaînes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stddef.h>
#include <stdio.h>
#include <string.h>


int main(void)
{
    char chaine[] = "un, deux, trois";
    char *p;
    unsigned i;

    p = strtok(chaine, ", ");

    for (i = 0; p != NULL; ++i)
    {
        printf("%u : %s\n", i, p);
        p = strtok(NULL, ", ");
    }

    return 0;
}
1
2
3
0 : un
1 : deux
2 : trois

La fonction strtok() étant un peu plus complexe que les autres, voici une petite marche à suivre pour réaliser cette fonction.

  • regarder si la chaîne fournie (ou la sous-chaîne suivante) contient, à son début, des caractères présent dans la seconde chaîne. Si oui, ceux-ci doivent être passés ;
  • si la fin de la (sous-)chaîne est atteinte, retourner un pointeur nul ;
  • parcourir la chaîne fournie (ou la sous-chaîne courante) jusqu’à rencontrer un des caractères composant la seconde chaîne. Si un caractère est rencontré, le remplacer par un caractère nul, conserver la position actuelle dans la chaîne et retourner la sous-chaîne ainsi créée (sans les éventuels caractères passés au début) ;
  • si la fin de la (sous-)chaîne est atteinte, retourner la (sous-)chaîne (sans les éventuels caractères passés au début).

Vous aurez besoin d’une variable de classe de stockage statique pour réaliser cette fonction. Également, l’instruction goto pourra vous être utile.

Correction

 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
34
35
36
char *xstrtok(char *chaine, char *liste)
{
    static char *dernier;
    char *base = (chaine != NULL) ? chaine : dernier;
    char *s;
    char *t;

    if (base == NULL)
        return NULL;

separateur_au_debut:
    for (t = liste; *t != '\0'; ++t)
        if (*base == *t)
        {
            ++base;
            goto separateur_au_debut;
        }

    if (*base == '\0')
    {
        dernier = NULL;
        return NULL;
    }

    for (s = base; *s != '\0'; ++s)
        for (t = liste; *t != '\0'; ++t)
            if (*s == *t)
            {
                *s = '\0';
                dernier = s + 1;
                return base;
            }

    dernier = NULL;
    return base;
}

Dans le chapitre suivant, nous aborderons le mécanisme de l’allocation dynamique de mémoire.