Confrontare Stringhe in Linguaggio C

Una delle operazioni più comuni che si possono fare con le stringhe in linguaggio C è il confronto tra due stringhe.

Confrontare due stringhe significa stabilire se una stringa è minore, maggiore o uguale a un'altra stringa. In linguaggio C, per fare ciò si utilizzano le funzioni strcmp e strncmp che sono definite nella libreria string.h.

In questa lezione vedremo come funzionano queste due funzioni e come sia possibile implementarle manualmente.

Ordine lessicografico e confronto di stringhe

Prima di poter vedere come sia possibile confrontare due stringhe in linguaggio C, è necessario soffermarsi su cosa vuol dire esattamente questa operazione.

Di solito, quando si parla di confrontare due sequenze di caratteri, o stringhe, si pensa immediatamente all'ordine alfabetico. Un po' come sono ordinate le parole all'interno di un dizionario.

In pratica, se abbiamo due parole, ad esempio albero e borsa, possiamo dire che albero è minore di borsa perché la lettera a, ossia la lettera iniziale di albero, viene prima in ordine alfabetico rispetto alla lettera b, che è la lettera iniziale di borsa:

\underline{\text{a}}\text{lbero} < \underline{\text{b}}\text{orsa}

Nel caso in cui, le parole che stiamo confrontando inizino con la stessa lettera, allora dobbiamo confrontare la seconda lettera, e così via, fino a quando non troviamo una differenza.

Ad esempio, se confrontiamo le parole albero e alba, possiamo dire che albero è maggiore di alba. Questo perché le due parole iniziano per le stesse tre lettere, a, l e b, ma la quarta lettera di albero è una e, mentre la quarta lettera di alba è una a. E dato che la lettera e viene dopo la lettera a in ordine alfabetico, possiamo dire che albero è maggiore di alba:

\textbf{alb}\underline{\text{e}}\text{ro} > \textbf{alb}\underline{\text{a}}

Da notare che il confronto alfabetico che abbiamo mostrato non dipende dalla lunghezza delle due parole. Infatti, se confrontiamo le parole albero e albergo, possiamo dire che albergo è minore di albero (infatti la lettera g viene prima della lettera o in ordine alfabetico), anche se la parola albergo è più lunga di albero.

In linguaggio C, così come nella stragrande maggioranza dei linguaggi di programmazione, il principio alla base del confronto tra stringhe è simile a quello che abbiamo appena descritto. Tuttavia, il confronto non viene fatto in base all'ordine alfabetico, ma in base all'ordine lessicografico.

Ordine lessicografico

Quando si confrontano due stringhe in linguaggio C si rispettano le seguenti regole:

  1. Una stringa s1 è minore di una stringa s2 se i primi i caratteri di s1 sono uguali ai primi i caratteri di s2, ma il carattere i+1 di s1 è minore del carattere i+1 di s2;
  2. Una stringa s1 è minore di una stringa s2 se tutti i caratteri di s1 sono uguali ai primi i caratteri di s2, ma s1 è più corta di s2.

Tralasciamo per un momento cosa voglia dire che un carattere è minore di un altro. Concentriamoci, adesso, sulle due regole che abbiamo appena elencato.

La prima regola, sostanzialmente, è una generalizzazione dell'ordinamento alfabetico che abbiamo visto prima. In pratica, si confrontano i caratteri delle due stringhe uno alla volta, partendo dal primo carattere. Se si trova una differenza tra due caratteri, allora si può dire quale delle due stringhe è maggiore.

La seconda regola, invece, riguarda il caso in cui una stringa rappresenta il prefisso dell'altra. In questo caso, la stringa più corta è minore di quella più lunga. Ad esempio, la stringa casa è minore di casale. Infatti, casale ha le stesse 4 lettere iniziali di casa, ma è più lunga.

Detto questo, adesso bisogna capire cosa voglia dire che un carattere è minore di un altro. In linguaggio C, i caratteri sono, essenzialmente, numeri. Ogni carattere è rappresentato da un numero intero, che è il suo codice ASCII. Quindi, quando si confrontano due caratteri, in realtà si confrontano i loro codici ASCII.

Del resto, una stringa in C non è composta da sole lettere, ma può contenere numeri, simboli e caratteri di controllo. In questo caso, il confronto tra due stringhe avviene confrontando i codici ASCII dei caratteri uno alla volta.

In base a ciò, il risultato è che il confronto tra due stringhe può portare a delle sorprese. Infatti, in ASCII i numeri sono rappresentati da codici che vanno da 48 a 57, mentre le lettere maiuscole sono rappresentate da codici che vanno da 65 a 90 e le lettere minuscole da 97 a 122. La conseguenza è che una cifra è minore di una lettera maiuscola, che a sua volta è minore di una lettera minuscola.

Ad esempio, la stringa 123ABC è minore della stringa ABC, perché il carattere 1 ha un codice ASCII minore del carattere A.

Analogamente la stringa ABC è minore della stringa abc, perché il carattere A ha un codice ASCII minore del carattere a.

Vediamo altri esempi:

"Casa" < "casa"
"123" < "ABC"
"123" < "abc"
"CASA" < "casa"
"casale" > "casa"
"casa" == "casa"

In questo meccanismo rientrano anche i caratteri di punteggiatura. Ad esempio lo spazio ha un codice ASCII minore di qualsiasi lettera o cifra. Quindi, la stringa Casa mia è minore della stringa Casale.

Queste regole prendono, collettivamente, il nome di ordine lessicografico.

Ricapitolando:

Definizione

Ordine lessicografico

In linguaggio C si usa l'ordine lessicografico per confrontare due stringhe. Una stringa s1 è minore di una stringa s2 se:

  1. I primi i caratteri di s1 sono uguali ai primi i caratteri di s2, ma il carattere i+1 di s1 è minore del carattere i+1 di s2;
  2. Tutti i caratteri di s1 sono uguali ai primi i caratteri di s2, ma s1 è più corta di s2.

Il confronto tra i singoli caratteri avviene confrontando i loro codici numerici ASCII.

Detto questo, possiamo passare a vedere come sia possibile confrontare due stringhe in linguaggio C.

Funzione strcmp - Confronto di stringhe

In linguaggio C, per confrontare due stringhe si utilizza la funzione strcmp che è definita nella libreria string.h.

La funzione strcmp accetta due stringhe come argomenti e restituisce un valore intero che rappresenta il risultato del confronto lessicografico tra le due stringhe.

La funzione strcmp ha la seguente firma:

int strcmp(const char *s1, const char *s2);

Dove s1 e s2 sono le due stringhe da confrontare. Il risultato del confronto è restituito come valore intero:

  • Se s1 è minore di s2, allora il valore restituito è minore di zero;
  • Se s1 è maggiore di s2, allora il valore restituito è maggiore di zero;
  • Se s1 è uguale a s2, allora il valore restituito è zero.

Vediamo un esempio:

#include <stdio.h>
#include <string.h>

int main() {
    char s1[] = "casa";
    char s2[] = "casale";

    int result = strcmp(s1, s2);

    if (result < 0) {
        printf("La stringa s1 è minore di s2\n");
    } else if (result > 0) {
        printf("La stringa s1 è maggiore di s2\n");
    } else {
        printf("Le due stringhe sono uguali\n");
    }

    return 0;
}

In questo esempio, la stringa s1 è minore di s2, quindi il risultato del confronto è minore di zero. Di conseguenza, il programma stampa a video il messaggio La stringa s1 è minore di s2.

Definizione

Funzione strcmp

La funzione strcmp è utilizzata per confrontare due stringhe in linguaggio C.

Essa è definita nella libreria string.h:

#include <string.h>

La funzione strcmp ha la seguente firma:

int strcmp(const char *s1, const char *s2);

Dove s1 e s2 sono le due stringhe da confrontare. Il risultato del confronto è restituito come valore intero:

  • Se s1 è minore di s2, allora il valore restituito è minore di zero;
  • Se s1 è maggiore di s2, allora il valore restituito è maggiore di zero;
  • Se s1 è uguale a s2, allora il valore restituito è zero.

Funzione strncmp - Confronto di stringhe con prefisso

La funzione strcmp confronta due stringhe per intero. Tuttavia, a volte può essere utile confrontare solo un prefisso delle due stringhe.

Per fare ciò, si può utilizzare la funzione strncmp che è definita nella libreria string.h.

La funzione strncmp accetta tre argomenti: due stringhe e un intero che rappresenta il numero di caratteri da confrontare.

La funzione strncmp ha la seguente firma:

int strncmp(const char *s1, const char *s2, size_t n);

Dove s1 e s2 sono le due stringhe da confrontare e n è il numero di caratteri da confrontare.

Per il resto, la funzione è simile alla funzione strcmp. Il risultato del confronto è restituito come valore intero e segue le stesse regole della funzione strcmp.

Vediamo un esempio:

#include <stdio.h>
#include <string.h>

int main() {
    char s1[] = "casa";
    char s2[] = "casale";

    /* Primo confronto: confronta solo i primi 4 caratteri */
    int result1 = strncmp(s1, s2, 4);

    if (result1 < 0) {
        printf("I primi 4 caratteri di s1 sono minori dei primi 4 caratteri di s2\n");
    } else if (result1 > 0) {
        printf("I primi 4 caratteri di s1 sono maggiori dei primi 4 caratteri di s2\n");
    } else {
        printf("I primi 4 caratteri delle due stringhe sono uguali\n");
    }

    /* Secondo confronto: confronta solo i primi 5 caratteri */
    int result2 = strncmp(s1, s2, 5);

    if (result2 < 0) {
        printf("I primi 5 caratteri di s1 sono minori dei primi 5 caratteri di s2\n");
    } else if (result2 > 0) {
        printf("I primi 5 caratteri di s1 sono maggiori dei primi 5 caratteri di s2\n");
    } else {
        printf("I primi 5 caratteri delle due stringhe sono uguali\n");
    }

    return 0;
}

In questo esempio, il programma effettua due confronti tra le stringhe s1 e s2:

  1. Il primo confronto confronta solo i primi 4 caratteri delle due stringhe. Il risultato del confronto è uguale a zero in quanto i primi 4 caratteri delle due stringhe sono uguali. Il programma stampa a video il messaggio I primi 4 caratteri delle due stringhe sono uguali.
  2. Il secondo confronto confronta i primi 5 caratteri delle due stringhe. Il risultato del confronto è minore di zero in quanto s1 è composta da soli 4 caratteri, mentre s2 è composta da 6. Di conseguenza, il programma stampa a video il messaggio I primi 5 caratteri di s1 sono minori dei primi 5 caratteri di s2.
Definizione

Funzione strncmp

La funzione strncmp è utilizzata per confrontare due stringhe in linguaggio C, considerando al più i primi n caratteri.

Essa è definita nella libreria string.h:

#include <string.h>

La funzione strncmp ha la seguente firma:

int strncmp(const char *s1, const char *s2, size_t n);

Dove s1 e s2 sono le due stringhe da confrontare e n è il numero di caratteri da confrontare. Il risultato del confronto è restituito come valore intero:

  • Se i primi n caratteri di s1 sono minori dei primi n caratteri di s2, allora il valore restituito è minore di zero;
  • Se i primi n caratteri di s1 sono maggiori dei primi n caratteri di s2, allora il valore restituito è maggiore di zero;
  • Se i primi n caratteri di s1 sono uguali ai primi n caratteri di s2, allora il valore restituito è zero.

Implementazione di strcmp e strncmp

A livello didattico, è sempre interessante vedere come funzionano le funzioni standard di un linguaggio di programmazione. In questo caso, vediamo come potrebbe essere implementata la funzione strcmp e la funzione strncmp.

Partiamo da una possibile implementazione della funzione strcmp che chiameremo mia_strcmp:

 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
int mia_strcmp(const char *s1, const char *s2) {
    char *p1 = s1;
    char *p2 = s2;

    /* Prima parte */
    while (*p1 != '\0' && *p2 != '\0') {
        if (*p1 < *p2) {
            return -1;
        } else if (*p1 > *p2) {
            return 1;
        }

        p1++;
        p2++;
    }

    /* Seconda parte */
    if (*p1 == '\0' && *p2 == '\0') {
        return 0;
    } else if (*p1 == '\0') {
        return -1;
    } else {
        return 1;
    }
}

L'implementazione è molto interessante. La funzione è divisa in due parti in quanto deve tener conto del caso in cui una stringa abbia una lunghezza minore dell'altra.

Nella prima parte, righe 6-15, la funzione confronta i caratteri delle due stringhe uno alla volta fintanto che i caratteri siano diversi dal terminatore, \0, e non vi sia una differenza. Se si trova una differenza, allora la funzione restituisce un valore intero che rappresenta il risultato del confronto. Altrimenti, i puntatori vengono incrementati.

La seconda parte, righe 18-24, si raggiunge quando si esce dal ciclo while, senza che sia stato restituito un valore tramite return. Ciò significa che si è verificato uno dei seguenti casi:

  1. Entrambe le stringhe sono terminate. In questo caso, le due stringhe sono uguali e la funzione restituisce zero; Infatti, se ci fosse stata una differenza, sarebbe stata rilevata nel ciclo while;
  2. La stringa s1 è terminata, ma la stringa s2 no. In questo caso, la stringa s1 è minore di s2 e la funzione restituisce un valore minore di zero;
  3. La stringa s2 è terminata, ma la stringa s1 no. In questo caso, la stringa s1 è maggiore di s2 e la funzione restituisce un valore maggiore di zero.

Da notare che la differenza tra i caratteri, alla righe 7 e 9, viene valutata in modo numerico, ossia semplicemente confrontando i codici ASCII dei due caratteri attraverso gli operatori di confronto < e >.

Passiamo ora all'implementazione della funzione strncmp che chiameremo mia_strncmp:

 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
int mia_strncmp(const char *s1, const char *s2, size_t n) {
    char *p1 = s1;
    char *p2 = s2;

    /* Prima parte */
    size_t i = 0;
    while (*p1 != '\0' && *p2 != '\0' && i < n) {
        if (*p1 < *p2) {
            return -1;
        } else if (*p1 > *p2) {
            return 1;
        }

        p1++;
        p2++;
        i++;
    }

    /* Seconda parte */
    if (i == n) {
        return 0;
    } else if (*p1 == '\0' && *p2 == '\0') {
        return 0;
    } else if (*p1 == '\0') {
        return -1;
    } else {
        return 1;
    }
}

L'implementazione della funzione mia_strncmp è simile a quella della funzione mia_strcmp. La differenza principale è che la funzione mia_strncmp confronta al più i primi n caratteri delle due stringhe.

Per prima cosa, abbiamo aggiunto un contatore i che tiene traccia del numero di caratteri confrontati. Questo contatore viene incrementato ad ogni iterazione del ciclo while (riga 16). Abbiamo modificato la condizione del ciclo while (riga 7) in modo che il ciclo termini quando si raggiunge il numero massimo di caratteri da confrontare, n.

La seconda parte della funzione, righe 20-28, è simile a quella della funzione mia_strcmp. Tuttavia, abbiamo aggiunto una condizione iniziale (riga 20) che controlla se il numero di caratteri confrontati è uguale a n. In tal caso, la funzione restituisce zero, indipendentemente dal fatto che le due stringhe siano terminate o meno.

In Sintesi

Abbiamo studiato in questa lezione che:

  • Il confronto tra due stringhe in linguaggio C avviene in base all'ordine lessicografico;
  • L'ordine lessicografico si basa sul confronto dei singoli caratteri delle due stringhe, partendo dal primo carattere;
  • Il confronto tra i singoli caratteri avviene confrontando i loro codici numerici ASCII;
  • In base all'ordine lessicografico, una stringa s1 è minore di una stringa s2 se i primi i caratteri di s1 sono uguali ai primi i caratteri di s2, ma il carattere i+1 di s1 è minore del carattere i+1 di s2;
  • Per confrontare due stringhe in linguaggio C si utilizza la funzione strcmp che restituisce un valore intero che rappresenta il risultato del confronto;
  • La libreria string.h fornisce anche la funzione strncmp che permette di confrontare solo un prefisso delle due stringhe.

Inoltre, abbiamo visto come potrebbero essere implementate le funzioni strcmp e strncmp in linguaggio C.