Le stringhe - Soluzione
In questo tutorial non verrà presentata la possibile soluzione nella sua interezza, ma soltano le parti interessanti, che dovranno essere opportunamente integrate per fornire la soluzione completa e compilabile.
Per ottenere un programma completo e funzionante, si tratta pertanto di scrivere la funzione main
che dichiara le variabili necessarie e che chiama opportunamente le funzioni descritte.
Estrazione del basename
La funzione che determina il basename può essere la seguente:
void basename(const char *percorso, char *b) {
int pos = trova_ultimo_separatore(percorso);
strcpy(b, percorso + pos + 1);
}
che opera come segue:
- prima viene trovato l'indice dell'ultimo separatore (
pos
) usandotrova_ultimo_separatore
- con
strcpy
viene copiata la porzione di stringa che va dapos + 1
fino alla fine della stringapercorso
all'interno della stringab
- questa istruzione funziona perché
percorso
è un indirizzo, ovvero l'indirizzo del primo carattere della stringa (percorso
è un puntatore achar
e viene usato per effettuare un passaggio di parametri per riferimento, vedi S6.6) - quindi spostandosi di
pos + 1
caratteri si ottiene l'indirizzo del primo carattere del basename - il tutto è reso possibile dal fatto che i caratteri sono memorizzati nella stringa in posizioni contigue della memoria (si ricordi che si tratta di un vettore, vedi S5.9.1)
- questa istruzione funziona perché
La funzione trova_ultimo_separatore
può essere definita come segue:
int trova_ultimo_separatore(const char *percorso)
{
int pos = strlen(percorso) - 1;
while (pos >= 0 && percorso[pos] != '/')
pos--;
return pos;
}
e può operare come segue:
pos
viene inizializzato pari all'indice dell'ultimo carattere della stringapercorso
, usando lastrlen
la quale restituisce la lunghezza della stringa in numero di caratteri (S6.11)- il ciclo
while
(S4.7) continua a decrementare di uno (pos--
) l'indice fintanto che non viene incontrato il primo separatore (partendo da destra) oppure si arriva al valore zero (si sono verificati tutti i caratteri della stringa) - viene restituito il valore di
pos
, che varrà0
se non ci sono separatori, oppure l'indicepos > 0
corrispondente al primo carattere'/'
incontrato (il più a destra)
Ricorda che
- l'operatore
&&
è l'AND logico (S11.5.1) - l'operatore
!=
significa "diverso da" (S11.4.1) - la costante
'/'
è una costante intera di tipochar
che corrisponde al carattere ASCII/
(S5.5)
Estrazione del dirname
Per l'estrazione del dirname si può usare la seguente funzione:
void dirname(const char *percorso, char *b) {
int i;
int n = trova_ultimo_separatore(percorso);
/* Scrittura del terminatore tenendo anche conto del caso n == -1 */
if (n >= 0) {
/* Copia in b i primi n caratteri del percorso */
for (i = 0; i < n; i++)
b[i] = percorso[i];
b[n] = '\0';
} else
b[0] = '\0';
}
Prima viene trovato l'indice n
dell'ultimo separatore usando la funzione trova_ultimo_separatore
illustrata nel punto precedente.
Se il valore di n
è positivo, si svolgono le seguenti operazioni:
- con il ciclo
for
(S4.6) si copiano i primin
caratteri dapercorso
ab
- con
b[n] = 0
si pone il carattere successivo all'ultimo copiato pari a zero, terminando la stringa
Altrimenti di pone a zero il primo carattere della stringa con b[0] = '\0'
, che corrisponde a impostare la stringa b
pari alla stringa vuota.
Questo significa che il percorso non contiene directory ma solo un nome di file.
ATTENZIONE: Ricorda che la costante '\0'
è una costante intera di tipo char
che corrisponde al carattere ASCII di valore numerico zero (S5.5).
Non è il carattere '0'
, che invece ha valore numerico 48 (T5.3).
Estrazione del tipo di percorso
La funzione può essere realizzata come segue:
int assoluto(const char *percorso)
{
if (percorso[0] == '/') return 1;
return 0;
}
Essa restituisce 1
(e quindi "vero") se il primo carattere della stringa percorso
, che è percorso[0]
, corrisponde al carattere '/'
, altrimenti restituisce 0
.
La return 1
termina la funzione, quindi la return 0
viene eseguita solo se il primo carattere percorso[0]
è diverso da '/'
.
Di conseguenza, in questo caso l'else
non serve.
Un esempio simile è riportato in T5.3.
Nel main
bisognerà stampare il valore corretto in output a seconda del valore restituito da assoluto
.
Estrazione dell'estensione
Per determinare l'estensione, conviene riformulare la funzione trova_ultimo_separatore
generalizzandola per cercare l'ultimo carattere, che specificato come argomento:
int trova_ultimo_carattere(const char *percorso, char c)
{
int pos = strlen(percorso) - 1;
while (pos >= 0 && percorso[pos] != c)
pos--;
return pos;
}
Questa operazione di "riformulazione" di una funzinalità esistente si chiama in gergo refactoring. Si notino le modifiche minime alla funzione originale.
Modifica il programma per usare la nuova funzione trova_ultimo_carattere
anche nelle altre funzioni già sviluppate.
La funzione trova_ultimo_carattere
può essere usata nella seguente funzione estensione
:
int estensione(char *percorso, char *ext) {
int pos_punto = trova_ultimo_carattere(percorso, '.');
int pos_sep = trova_ultimo_carattere(percorso, '/');
int len = strlen(percorso);
/* il punto più a destra è a sinistra del separatore più a destra */
if (pos_punto < pos_sep)
return 0;
/* il punto più a destra è l'ultimo carattere della stringa (no estensione) */
if (pos_punto + 1 == len)
return 0;
/* c'è almeno un punto nel percorso */
if (pos_punto > 0) {
strcpy(ext, percorso + pos_punto + 1);
return 1;
}
return 0;
}
la quale opera nel modo seguente:
- usando la funzione
trova_ultimo_carattere
, trova la posizione dell'ultimo punto (pos_punto
) e dell'ultimo separatore (pos_sep
) - il primo
if
causa la terminazione dalla funzione segnalando che non c'è estensione - restituendo0
conreturn 0
- se il carattere punto più a destra rimane a sinistra del separatore più a destra (es./home/dir.con.punto/file
); altrimenti... - il secondo
if
esce dalla funzione segnalando che non c'è estensione - restituendo0
- se il punto più a destra è l'ultimo carattere della stringa (es./home/utente/nomefile.
, dove ovviamente non c'è estensione); altrimenti... - il terzo
if
copia l'estensione della stringa di uscitaext
se c'è un punto nel percorso e restituisce1
(attenzione che si arriva a questo punto quando le condizioni dei precedenti dueif
sono false) - altrimenti termina senza trovare un'estensione, con l'ultimo
return 0
Analisi completa
Nel calcolo di basename e dirname bisogna fare attenzione ad alcuni casi particolari:
- se il percorso indica un nome di file "puro", cioè senza alcun separatore, allora il suo dirname è la directory corrente
.
(punto) - se il percorso termina in coda con uno o più separatori, essi devono essere eliminati sia dal dirname che dal basename (attenzione che in questo caso il basename è corriponde al nome di una directory, non di un file; questo però non cambia nulla nella suddivisione tra dirname e basename)
- se il percorso coincide con la directory speciale
/
, allora sia il basename che il dirname sono pari a/
- se c'è un unico separatore, e si trova all'inizio del percorso, allora il dirname è la directory radice
/
Si modifichi il programma in modo che basename e dirname tengano in considerazione questi casi particolari. I nuovi elementi calcolati dovranno essere stampati dopo i marcatori:
[BASENAME_COMPLETO] e [DIRNAME_COMPLETO]
in modo da consentire la verifica tramite il comando
./pvcheck -f percorsi2.test ./percorsi
utilizzando il file di test percorsi2.test