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 con lung)
  • usa un ciclo while che faccia partire un contatore (es. pos) da lung e lo decrementi (con pos--) 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 stringa b usando la strcpy (vedi S5.11)
  • modifica la stringa b ponendo b[sep] = 0; in questo modo, i caratteri significativi di b vengono "troncati" al carattere di indice sep, poiché lo 0 diviene il carattere di terminazione della stringa b (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.