Eliminazione estensione

Questo esercizio consiste nella eliminazione della estensione dal nome di un file. Il nome di un file è una stringa, che può contenere il carattere punto '.' Lo scopo di questo esercizio è realizzare un programma che prende una stringa da linea di comando, e stampa la parte iniziale della stringa fino al carattere punto '.'; se questo carattere non c'è, si deve stampare tutta la stringa (il fatto che la stringa passata è il nome di un file è in effetti irrilevante).

Per prima cosa, il programma deve poter usare gli argomenti passati da linea di comando. Per questa ragione, la intestazione della funzione main deve contenere l'intero e il vettore di stringhe:

int main(int argn, char *argv[]) ...

Il programma deve venire chiamato con un singolo argomento, e questo si può controllare vedendo se il numero di argomenti passati, che è argn-1, coincide o meno con 1. In caso contrario, si stampa un messaggio di errore e si esce.

A questo punto, sappiamo che il programma è stato lanciato con un solo argomento. Il primo argomento si trova come sempre memorizzato nella stringa argv[1]. Si ricorda che:

una stringa è un vettore di caratteri che contiene il carattere '\0' che indica che i successivi caratteri non sono significativi.

In altre parole, una stringa è un vettore di caratteri, che può essere sia statico che dinamico. I caratteri compresi fra il primo e il carattere '\0' sono quelli significativi della stringa. Si dice anche che il carattere '\0' è il terminatore di stringa. Dal momento che questo è il modo per indicare dove la stringa finisce, questo carattere deve apparire almeno una volta nel vettore.

Nel nostro caso, sappiamo che argv[1] è la stringa che ci interessa. In particolare, questa variabile contiene un puntatore a carattere, che si può appunto considerare come un vettore allocato dinamicamente. Il vettore argv è un vettore di puntatori, ossia un vettore i cui elementi sono puntatori. Il punto fondamentale è che ogni elemento del vettore è un puntatore a caratteri. In particolare, argv[1] punta a una zona di memoria in cui si trovano, in ordine, i caratteri che compongono il primo argomento con cui il programma è stato chiamato. Inoltre, sappiamo che questa sequenza di caratteri è una stringa, ossia la sua parte significativa viene seguita immediatamente dal carattere di fine stringa '\0'.

Per capire meglio la situazione, vediamo come è strutturata le memoria quando il programma viene chiamato con un singolo argomento, e supponiamo che questo primo argomento sia esempio.txt. In altre parole, vediamo cosa succede all'avvio del programma quando viene lanciato per stampare il nome di file esempio.txt senza estensione.

Quello che succede è che la sequenza di caratteri esempio.txt viene memorizzata in una zona di memoria, seguita dal singolo carattere '\0' (questo lo si vede leggendo i caratteri nella figura dal basso verso l'alto). Questo è il modo standard di memorizzare una stringa. Il vettore argv contiene un insieme di puntatori, ognuno dei quali punta all'inizio della zona di memoria in cui è memorizzata una stringa. In particolare, argv[1] punta all'inizio della zona dove si trova la stringa esempio.txt.

Dal momento che argv[1] è un puntatore a carattere, lo si può interpretare come un vettore dinamico, per cui argv[1][0] è il carattere puntato da argv[1], mentre argv[1][1] è il successivo, argv[1][2] quello ancora dopo, ecc.

In questo modo possiamo accedere agli elementi di tutta la stringa. Infatti, sappiamo ora che il primo elemento della stringa è argv[1][0], sappiamo come accedere ai successivi, e sappiamo che la stringa termina dove si incontra il carattere '\0'.

A questo punto, abbiamo tutti i dati che ci servono per realizzare il facile programma di eliminazione della estensione. Tutto quello che dobbiamo fare è leggere in ordine i caratteri nel vettore dinamico argv[1] fino a che non si incontra il carattere punto '.', oppure si arriva alla fine della stringa (ossia si arriva al carattere nullo '\0'.

Usiamo quindi una variabile i per fare la scansione della stringa. Dal momento che il primo carattere si trova nel primo elemento del vettore, che ha indice 0, questa variabile parte da zero. Dato che questa variabile deve ogni volta indicare un carattere successivo del vettore, la incrementiamo ad ogni passo. In particolare, ad ogni passo stampiamo il carattere indicato da i e incrementiamo la variabile i in modo tale che alla successiva iterazione del ciclo si passa al carattere successivo. L'uscita dal ciclo deve avvenire se si incontra il carattere punto '.' oppure la fine della stringa, cioè il carattere nullo '\0'.

                /* ciclo di stampa */
  i=0;
  while( argv[1][i]!=0 && argv[1][i]!='.' ) {
    printf("%c", argv[1][i]);
    i++;
  }

Dal momento che la condizione di uscita è l'opposto della condizione che ci fa stare nel ciclo, la condizione del while è che il carattere sia diverso dal punto e dal fine stringa. In altre parole, finchè il carattere corrente è diverso dal punto e dal carattere nullo, dobbiamo continuare a eseguire il ciclo.

Il programma completo noext.c è riportato qui sotto.

/*
  Elimina da un nome di file la sua estensione.
*/

#include<stdlib.h>
#include<stdio.h>

int main(int argn, char *argv[]) {
  int i;


  		/* controllo argomenti */
  if( argn-1!=1 ) {
    printf("Numero errato di argomenti\n");
    exit(1);
  }


		/* ciclo di stampa */
  i=0;
  while( argv[1][i]!=0 && argv[1][i]!='.' ) {
    printf("%c", argv[1][i]);
    i++;
  }

  printf("\n");


  return 0;
}


Una possibile variante del programma memorizza la parte iniziale della stringa (fino al primo punto), in una seconda stringa. Questo può essere utile se per esempio serve accedere a un file che ha la stessa parte iniziale del nome ma una estensione diversa.

La soluzione non differisce di molto dalla prima. Intanto, ci serve una stringa per memorizzare la parte iniziale della stringa. Usiamo per esempio un vettore statico, e assumiamo che bastino cento caratteri. Il programma deve copiare i caratteri, in ordine, dalla stringa argv[1] al vettore di caratteri da noi dichiarato. Questa operazione di copiatura termina quando si verifica una di queste tre cose:

  1. la stringa argv[1] è finita;
  2. abbiamo trovato il carattere punto nella stringa argv[1];
  3. non c'è più spazio nel vettore

Bisogna ora fare attenzione al fatto che, in tutti e tre i casi, il vettore su cui copiamo la stringa deve contenere, alla fine della parte significativa, il carattere di terminazione. Questo implica che la parte di vettore su cui si possono scrivere i caratteri non sono 100, ma solo 99, visto che l'ultimo serve per il terminatore. Questo significa che, nel terzo caso, non dobbiamo fermarci al carattere di indice 100-1, ma al carattere di indice 99-1 (l'ultimo carattere del vettore è quello di indice 100-1, ma questo carattere va lasciato libero per metterci il terminatore).

Dopo aver copiato la stringa, occorre scrivere il terminatore di stringa. Dal momento che quando si esce il valore di i è l'indice del primo elemento libero dell'array, questa è la posizione in cui va scritto il terminatore.

Il programma completo noextcopy.c è qui sotto.

/*
  Elimina da un nome di file la sua estensione.
*/

#include<stdlib.h>
#include<stdio.h>

int main(int argn, char *argv[]) {
  char senzaext[100];
  int i;


  		/* controllo argomenti */
  if( argn-1!=1 ) {
    printf("Numero errato di argomenti\n");
    exit(1);
  }


		/* copia l'argomento in senzaext fino a . oppure alla fine */
  i=0;
  while( argv[1][i]!=0 && argv[1][i]!='.' && i<=100-1-1 ) {
    senzaext[i]=argv[1][i];
    i++;
  }
  senzaext[i]=0;


		/* stampa il nome senza estensione */
  printf("%s\n",senzaext);


  return 0;
}


Un'altra facile variante di questo esercizio consiste nel copiare su stringa non la parte iniziale della stringa, ma solo la estensione, ossia la parte che segue il primo carattere punto '.' che si incontra.