[C] Costrutire un vettore di stringhe

Messaggioda Jezeph » 16/02/2017, 19:49

Ciao a tutti, sto cercando di risolvere un esercizio ma non ci riesco in nessun modo. Ecco il testo:

Un file di testo riporta i risultati di una serie di partite di pallacanestro, una partita per ogni riga di testo. Il formato è il seguente:
nome_squadra_1 vs nome_squadra_2 punti1 - punti2
dove
• nome squadra 1 e nome squadra 2 sono stringhe che identificano i nomi delle due squadre; le stringhe non contengono
spazi;
• vs (sta per versus) è il separatore dei due nomi;
• punti1 e punti2 sono rispettivamente il punteggio finale della prima e della seconda squadra; sono numeri interi maggiori
o uguali a zero.
Il numero di righe non è noto a priori, mentre la lunghezza massima di ciascuna stringa è pari a 80 caratteri. Il file contiene
almeno una linea.
Stampare il nome di tutte le squadre che compaiono nel file. Il nome di ogni squadra deve essere stampato una sola volta,
nell’ordine di apparizione della squadra nel file.
Stampare il risultato con il seguente formato:
[SQUADRE]
nome_squadra_1
nome_squadra_2
nome_squadra_3
...


Il suddetto file di testo è il seguente:
Rockets vs Warriors 102 - 91
Lakers vs Pistons 78 - 89
Pistons vs Rockets 90 - 88
Warriors vs Celtics 102 - 90
Clippers vs Warriors 68 - 72


Ora, ecco il codice che ho scritto:

Codice:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct partita {
   char sq1[20];
   char sq2[20];
   int pt1;
   int pt2;
};

int search(int nsq, char *nomisq, char *x)
{
   int j=0;

   while ((j<nsq) && (strcmp(nomisq[j], x)))
      j++;

   if (j<nsq) //successo, corrispondenza trovata
      return 0;
   else   //insuccesso, corrispondenza non trovata
      return 1;
}   

int main (int argc, char *argv[])
{
   struct partita *vect;
   FILE *infile;
   char buf[81];
   int npart = 5; //numero di partite nel file (righe da leggere). 5 provvisorio, se necessario rialloco
   int i = 0, tok, j; //numero righe lette, numero token convertiti, gestione cicli   
   
   if (argc != 2) { //controllo sulla correttezza degli argomenti del main
      printf("inserire come unico argomento il nome del file da leggere\n");
      return 2;
   }

   if (!(infile = fopen(argv[1], "r"))) { //apertura del file + controllo
      printf("impossibile aprire il file specificato\n");
      return -1;
   }
 
   if (!(vect = malloc(npart * sizeof(*vect)))) { //allocazione memoria + controllo
      printf("impossibile allocare memoria per la creazione delle strutture\n");
      return 1;
   }

   /* Ciclo di lettura */
   while (fgets(buf, sizeof(buf), infile)) {
      tok = sscanf(buf, "%s vs %s %d - %d", vect[i].sq1, vect[i].sq2, &vect[i].pt1, &vect[i].pt2);
      if (tok != 4) { //controllo sul numero di token convertiti
         printf("errore scansione token\n");
         return -1;
      }

      if (i+1 >= npart) { //controllo numero di righe lette
         npart *= 2;
         if (!(vect = realloc(vect, npart * sizeof(*vect)))) {
            free(vect);
            printf("impossibile riallocare memoria per la creazione delle strutture\n");
            return 1;
         }
      }
      i++;
   }

   vect = realloc(vect, i * sizeof(*vect)); //sapendo ora esattamente quante righe devo leggere, rialloco la memoria una ultima volta per evitare sprechi

   /* Ciclo di stampa delle strutture */
   printf("STRUTTURE LETTE:\n");
   for (j=0; j<i; j++) {
      printf("%s v %s: %d - %d\n", vect[j].sq1, vect[j].sq2, vect[j].pt1, vect[j].pt2);
   }
   printf("******************\n\n");

/* 4) Squadre */
   int nsq = (i * 2); //Non conosco a priori il numero di squadre, ma al limite possono essercene due nuove a ogni partita (i è il numero di partite giocate)
   char *nomisq[nsq]; //Questo vettore di (puntatori a) stringhe conterrà le diverse squadre. Le prime due le inizializzo io, tanto sono diverse per forza
   nomisq[0] = vect[0].sq1;
   nomisq[1] = vect[0].sq2;
   char *ptr;
   ptr = nomisq+2; //ptr punta al terzo elemento di nomisq

   for(j=1; j<i; j++) {   
      if(search(nsq, nomisq, vect[j].sq1)) {
         *ptr = vect[j].sq1;
         ptr++;         
      }
      if(search(nsq, nomisq, vect[j].sq2)) {
         *ptr = vect[j].sq2;
         ptr++;         
      }
   }

   printf("[SQUADRE]\n");
   for(j=1; j<nsq; j++) {
      printf("%s\n", nomisq[j]);
   }   
   
   free(vect);
   fclose(infile);
   return 0;
}


Sono riuscito a scrivere correttamente il codice per la lettura e l'immagazzinamento dei file in un array di strutture, che infatti vengono stampate a video correttamente così come sono scritte nel file.
Per la stampa dei nomi delle squadre invece, ho pensato di creare un array di stringhe, che viene a mano a mano riempito. I primi due elementi li ho inizializzati a mano, perchè le prime due squadre (della prima partita) sono necessariamente diverse. Poi ho puntato un puntatore al terzo elemento.
Per il resto ho pensato: un ciclo for scansiona una a una tutte le strutture (cioè tutte le partite). Per ogni j-esima partita quindi chiama la funzione search, la quale implementa un semplice algoritmo di ricerca: scansiona tutto il mio array nomisq e confronta ogni elemento con quello passato come argomento (una delle due squadre della j-esima partita): se trova corrispondenza riporta 0, altrimenti 1. Se non viene trovata corrispondenza significa che quella squadra non è ancora presente nel vettore, e quindi va aggiunta: l'elemento puntato da ptr deve allora venire aggiornato, puntando a quella squadra. Poi ptr viene fatto puntare allo slot successivo di nomisq (ptr++). Se invece la search riporta un valore nullo (ovvero c'è già un nome uguale nel vettore), non si fa nulla, ma si passa al prossimo nome.

Ecco il risultato della compilazione:

marco@Marco-VirtualBox:~/Documenti/22-03-2016$ gcc -Wall -o Svolg220316 Svolg220316.c
Svolg220316.c: In function ‘search’:
Svolg220316.c:16:28: warning: passing argument 1 of ‘strcmp’ makes pointer from integer without a cast [-Wint-conversion]
while ((j<nsq) && (strcmp(nomisq[j], x)))
^~~~~~
In file included from Svolg220316.c:3:0:
/usr/include/string.h:140:12: note: expected ‘const char *’ but argument is of type ‘char’
extern int strcmp (const char *__s1, const char *__s2)
^~~~~~
Svolg220316.c: In function ‘main’:
Svolg220316.c:121:6: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
ptr = nomisq+2; //ptr punta al terzo elemento di nomisq
^
Svolg220316.c:124:18: warning: passing argument 2 of ‘search’ from incompatible pointer type [-Wincompatible-pointer-types]
if(search(nsq, nomisq, vect[j].sq1)) {
^~~~~~
Svolg220316.c:12:5: note: expected ‘char *’ but argument is of type ‘char **’
int search(int nsq, char *nomisq, char *x)
^~~~~~
Svolg220316.c:125:9: warning: assignment makes integer from pointer without a cast [-Wint-conversion]
*ptr = vect[j].sq1;
^
Svolg220316.c:128:18: warning: passing argument 2 of ‘search’ from incompatible pointer type [-Wincompatible-pointer-types]
if(search(nsq, nomisq, vect[j].sq2)) {
^~~~~~
Svolg220316.c:12:5: note: expected ‘char *’ but argument is of type ‘char **’
int search(int nsq, char *nomisq, char *x)
^~~~~~
Svolg220316.c:129:9: warning: assignment makes integer from pointer without a cast [-Wint-conversion]
*ptr = vect[j].sq2;
^



Sapreste dirmi cosa sbaglio?
Jezeph
Starting Member
Starting Member
 
Messaggio: 19 di 40
Iscritto il: 01/01/2015, 22:17

Re: [C] Costrutire un vettore di stringhe

Messaggioda apatriarca » 16/02/2017, 21:43

Ci sono un sacco di errori relativi alla differenza tra `char **` e `char *`. Sono entrambi puntatori, ma sono ovviamente due tipi diversi. Non puoi utilizzarne uno dove è richiesto l'altro. Come prima approssimazione (considerando l'uso che ne stai facendo) possiamo dire che il primo corrisponde ad un array di stringhe e l'altro ad una singola stringa.

La funzione `search` serve a cercare una stringa all'interno di un array di stringhe. Nel suo prototipo dovranno quindi apparire entrambi i tipi considerati sopra, `char *` sarà usato per la stringa e `char **` sarà usato per l'array di stringhe. In particolare avremo:

Codice:
int search(int nsq, char **nomisq, char *x)


In alternativa puoi anche inserire la dimensione del tuo array nel seguente modo

Codice:
int search(int nsq, char *nomisq[nsq], char *x)


Discorso simile vale per la variabile ptr nel main. Deve essere di tipo `char **` in quanto puntatore a stringhe. Corretti questi errori il programma non arriva comunque alla fine. Ti lascio il compito di trovare gli altri problemi.
apatriarca
Moderatore
Moderatore
 
Messaggio: 4541 di 10435
Iscritto il: 08/12/2008, 20:37
Località: Madrid

Re: [C] Costrutire un vettore di stringhe

Messaggioda Jezeph » 17/02/2017, 11:07

Ciao, ti ringrazio per la risposta. Alla fine sono riuscito ad "aggirare" il problema omettendo completamente la funzione search, e implementando le sue istruzioni direttamente nel main:

Codice:
/* 4) Squadre */
   int nsq = (i * 2); //Non conosco a priori il numero di squadre, ma al limite possono essercene due nuove a ogni partita (i è il numero di partite giocate)
   char *nomisq[nsq]; //Questo vettore di (puntatori a) stringhe conterrà le diverse squadre. Le prime due le inizializzo io, tanto sono diverse per forza
   nomisq[0] = vect[0].sq1;
   nomisq[1] = vect[0].sq2;
   int k=2, m=0; // gestione cicli

   for(j=1; j<i; j++) { //scansiona tutte le partite e cerca le corrispondenze
      while((m<k) && (strcmp(nomisq[m], vect[j].sq1)))
         m++;
      if (!(m<k)) { //non ha trovato corrispondenza in nomisq, il nome è da aggiungere
         nomisq[k] = vect[j].sq1;
         k++;
      }
      m=0;
      while((m<k) && (strcmp(nomisq[m], vect[j].sq2)))
         m++;
      if (!(m<k)) { //non ha trovato corrispondenza in nomisq, il nome è da aggiungere
         nomisq[k] = vect[j].sq2;
         k++;
      }
      m=0;         
   }


Sostanzialmente c'è una variabile int k che utilizzo per puntare manualmente di volta in volta la prima cella "libera" nel vettore di puntatori a stringhe, incrementandola solo quando occupo quella cella.
Per quanto riguarda i miei errori, intendi dire che **char è un "puntatore a un puntatore a carattere"? Perchè avrei un puntatore al primo elemento di un array (nomisq), che a sua volta è un puntatore, quindi char **nomisq. Per quanto riguarda x invece, è una stringa che io passo alla funzione, quindi è semplicemente puntatore a carattere: char *x.
Jezeph
Starting Member
Starting Member
 
Messaggio: 20 di 40
Iscritto il: 01/01/2015, 22:17


Torna a Informatica

Chi c’è in linea

Visitano il forum: Nessuno e 1 ospite