Le stringhe
Nei sistemi operativi della famiglia Unix i file sono indicati tramite percorsi di directory delimitate dal carattere '/'
.
Spesso è utile poter decomporre un percorso nelle sue componenti.
Di particolare interesse sono:
- il basename del percorso, cioè il nome "puro" del file, senza le componenti che rappresentano le directory
- il dirname, ovvero il percorso completo senza il nome del file
- il tipo del percorso, che può essere assoluto (se inizia con il carattere
'/'
) oppure relativo - l'estensione del file, che è la parte del nome di un file dopo l'ultimo carattere
'.'
(punto)
Ad esempio, nel caso del file /home/pippo/percorsi.c
, si ha che:
- il basename è
percorsi.c
- il dirname è
/home/pippo
- il percorso è di tipo assoluto
- l'estensione è
c
Analisi semplificata di un percorso
Si scriva un programma C che esamini un percorso fornito come dato di ingresso.
Il percorso deve essere passato al programma come argomento sulla riga di comando.
Per esempio, se il programma compilato è a.out
, esso deve poter essere eseguito col comando
./a.out /home/pippo/percorsi.c
Si assuma che il percorso non superi i 1024
caratteri.
Suggerimenti
Per poter leggere il percorso da linea di comando, la funzione main
deve essere definita come int main(int argc, char *argv[])
(vedi S6.9).
Il parametro argv[1]
(vedi S6.9.1) conterrà la stringa fornita come argomento.
Realizza una funzione per il calcolo di ciascuno dei risultati richiesti. Vedi S6.3 per la dichiarazione di funzioni e S6.5, S6.6 e S6.7 per il passaggio dei parametri alle funzioni. Tieni conto dei seguenti aspetti:
- le funzioni dovranno essere richiamate nel
main
oppure da altre funzioni (se necessario) - la stringa contenente il percorso dovrà essere passata a tali funzioni, che restituiranno il risultato calcolato
- i dettagli su come impostare le varie funzioni verranno forniti di seguito
1) Basename
Determinare il basename e stamparlo col seguente formato:
[BASENAME]
percorsi.c
Suggerimenti
Per iniziare, conviene realizzare una funzione la quale, ricevuto un percorso come argomento, restituisca l'indice dell'ultimo separatore (il carattere /
) presente nella stringa, oppure un valore negativo se nessun separatore è presente.
La funzione può essere dichiarata come segue:
int trova_ultimo_separatore(const char *percorso);
Per trovare l'ultimo separatore, scrivi un ciclo che parte dall'ultimo carattere della stringa e, procedendo all'indietro, si fermi quando incontra il separatore, restituendo l'indice del carattere nella stringa. Per fare questo:
- usa la funzione
strlen
(vedi S5.11) per determinare l'indice corrispodente all'ultimo carattere della stringa (indichiamolo conlung
) - usa un ciclo
while
che faccia partire un contatore (es.pos
) dalung
e lo decrementi (conpos--
) fino ad incontrare il primo separatore partendo da destra (quindi l'ultimo della stringa) - il ciclo deve terminare se
pos
diviene negativo (nessun separatore è presente)
Alla fine, la funzione deve restituire il valore di pos
.
Crea una funzione basename
la quale, usando la funzione trova_ultimo_separatore
per determinare la posizione del separatore, restituisce il basename copiandolo in una stringa che viene restituita tramite passaggio per indirizzo.
La funzione basename sia dichiarata come segue:
void basename(const char *percorso, char *b);
Puoi usare strcpy
(vedi S5.11) per copiare il basename nella stringa da restituire.
IMPORTANTE: Entrambe le stringhe passate per indirizzo devono essere dichiarate nella funzione chiamante (il main
).
2) Dirname
Determinare il dirname e stamparlo col seguente formato:
[DIRNAME]
/home/pippo
Suggerimenti
Dichiara una funzione dirname
come segue:
void dirname(const char *percorso, char *b);
All'interno della funzione dirname
puoi utilizzare la funzione trova_ultimo_separatore
spiegata al punto precedente per trovare l'indice del carattere corrispondente all'ultimo separatore (chiamiamo sep
l'indice trovato).
Un modo per copiare il dirname nella stringa b
è il seguente:
- copia l'intera stringa
percorso
nella stringab
usando lastrcpy
(vedi S5.11) - modifica la stringa
b
ponendob[sep] = 0
; in questo modo, i caratteri significativi dib
vengono "troncati" al carattere di indicesep
, poiché lo0
diviene il carattere di terminazione della stringab
(il concetto di carattere di terminazione è spiegato in S5.11)
3) Tipo
Determinare il tipo di percorso, relativo oppure assoluto, e stampare rispettivamente relativo
oppure assoluto
col seguente formato:
[TIPO]
assoluto
Suggerimenti
Puoi dichiarare una funzione chiamata assoluto
come segue:
int assoluto(const char *percorso);
La funzione dovrà:
- ritornare
1
(a significare "vero") in caso si tratti di un persorso assoluto - ritornare
0
(a significare "falso") in caso contrario
Utilizza la funzione nel main
per stampare il risultato richiesto a seconda del valore restituito dalla funzione assoluto
.
Per capire se un percorso è assoluto o relativo basta confrontare il primo carattere della stringa (cioè percorso[0]
) con il carattere /
: se sono uguali allora il percorso è assoluto, altrimenti è relativo.
Ricorda che il confronto tra caratteri viene effettuato usando l'operatore ==
.
NOTA: quando si decide un nome per una funzione che debba ritornare un valore vero o falso (1
o 0
), è bene pensare a come si usa la funzione in una istruzione del tipo
if (assoluto(percorso)) { ... }
che va letta come "se percorso
è assoluto allora ...".
In questo caso il nome scelto è corretto dal punto di vista semantico nella frase precedente.
Sarebbe andato bene anche il nome relativo
, ovviamente avendo cura di invertire il valore restituito dalla funzione.
Non sarebbe andato bene un nome come tipo_di_percorso
, perché dal punto di vista semantico "tipo_di_percorso" non è vero/falso, ma può essere relativo/assoluto.
4) Estensione
Determinare l'estensione del file e stamparla con il seguente formato:
[ESTENSIONE]
c
Se l'estensione non è presente, stampare
[ESTENSIONE]
nessuna
Suggerimenti
Puoi realizzare una funzione che restituisce l'indice dell'ultimo punto della stringa (simile a quella realizzata per trovare l'ultimo separatore), chiamandola per esempio trova_ultimo_punto
.
Fai attenzione che anche i nomi delle directory possono contenere il carattere punto.
Devi quindi gestire opportunamente il caso in cui sia presente un file che non ha estensione mentre una delle directory precedenti include il punto nel proprio nome! (es. /home/utente.con.punto/mio_file
).
In pratica, devi controllare che l'ultimo punto nel percorso compaia dopo l'ultimo separatore.
Puoi creare una funzione estensione
la quale, usando la funzione trova_ultimo_punto
per determinare la posizione del punto, restituisce l'estensione copiandola in una stringa che viene restituita tramite passaggio per indirizzo.
La funzione estensione può essere dichiarata come segue:
void estensione(const char *percorso, char *b);
Usa la funzione strcpy
(vedi S5.11) per copiare l'estensione nella stringa da restituire b
Fai attenzione al fatto che entrambe le stringhe passate per indirizzo devono essere dichiarate nella funzione chiamante (il main
).
ATTENZIONE: se sono stati seguiti i suggerimenti fino a questo punto, sono state create due funzioni trova_ultimo_separatore
e trova_ultimo_punto
che sono praticamente identiche nel funzionamento, e si differenziano soltanto per quanto riguarda il carattere cercato.
Riformulare il programma sostituendo queste due funzioni con una sola funzione chiamata
int trova_ultimo_carattere(const char *percorso, char c);
la quale cerca l'ultimo generico carattere c
nella stringa percorso
.
Questa funzione si può usare per cercare sia l'ultimo separatore che l'ultimo punto, in questo modo:
ultimo_sep = trova_ultimo_carattere(percorso, '/');
ultimo_punto = trova_ultimo_carattere(percorso, '.');
In questo modo il programma è più elegante, visto che si evita la duplicazione di codice (due funzioni invece di una) che può essere invece incluso in una unica funzione opportunamente parametrizzata.
Verifica automatica
Utilizza il tool pvcheck
di verifica automatica per testare il corretto funzionamento del programma (maggiori informazioni circa l'uso di pvcheck
sono disponibili qui).
Il file contenente i test è percorsi1.test.
Il comando da eseguire per il test è il seguente:
pvcheck -f percorsi1.test ./a.out
Nella prossima pagina potrai esaminare un esempio di soluzione dell'esercizio.