[C] Problema con strtok

Forum dedicato alla programmazione.

Moderatore: Staff

Regole del forum
1) Citare in modo preciso il linguaggio di programmazione usato.
2) Se possibile portare un esempio del risultato atteso.
3) Leggere attentamente le risposte ricevute.
4) Scrivere i messaggi con il colore di default, evitare altri colori.
5) Scrivere in Italiano o in Inglese, se possibile grammaticalmente corretto, evitate stili di scrittura poco chiari, quindi nessuna abbreviazione tipo telegramma o scrittura stile SMS o CHAT.
6) Appena registrati è consigliato presentarsi nel forum dedicato.

La non osservanza delle regole porta a provvedimenti di vari tipo da parte dello staff, in particolare la non osservanza della regola 5 porta alla cancellazione del post e alla segnalazione dell'utente. In caso di recidività l'utente rischia il ban temporaneo.
Rispondi
elvis
Linux 0.x
Linux 0.x
Messaggi: 53
Iscritto il: lun 27 set 2010, 10:35
Slackware: 13.1 x64
Desktop: KDE 4.5.2
Distribuzione: Opensuse 11.3 x86

[C] Problema con strtok

Messaggio da elvis »

Salve, all'interno di un programma (che non ricopio per intero) ad un certo punto ho bisogno di una funzione che mi prenda una stringa e me la suddivida in un vettore di stringhe (vettore di dimensione N nota).
La stringa ha questo formato: "testo0#testo1#...#testoN-1#. Potrebbe però anche non avere del testo tra un # e un altro. (esempio: "asd#rotfl#lol##qwerty#asd##fine#" ).

Il formato mi ha subito fatto pensare all'uso di strtok, il problema però è che se incontra il "testo nullo" da quanto ho capito strtok va avanti a cercare un prossimo carattere buono per assegnarlo al prossimo token, mentre io avrei bisogno che quel testo vuoto corrisponda ad un puntatore NULL nella cella corrispondente del vettore .

Per farmi capire meglio, se la stringa è "asd##fine# il vettore di stringhe che mi serve è questo:

v[0] = "asd"
v[1] = NULL
v[2] = "fine"

mentre invece strtok mi "taglia" il token contenente il testo nullo, e alla fine ho:

v[0] = "asd"
v[1] = "fine"

Il pezzo di codice incriminato è questo:

Codice: Seleziona tutto

char **array_answer(char *answ, int N)    // Funzione che suddivide la stringa answ in un vettore di sottostringhe 
{
   int i;
 
   char *temp = malloc(strlen(answ)*sizeof(char));  // Alloca memoria per una copia di answ
   
   char *cpy = strcpy(temp, answ);   // copia answ nella memoria appena allocata
   
   char *buffer;  // variabile per allocare memoria per ogni sottostringa di answ
   
   char **risposte = malloc(N*sizeof(char *));  // vettore di stringhe che conterrà i vari token di answ
   
   char *token = strtok(cpy, "#");  // Comincia a dividere la copia di answ
   
   for (i = 0; token != NULL; i++) {  // Iterazione per memorizzare tutti i token nel vettore di stringhe
      
      if (strlen(token) > 0) {  // se il token contiene del testo...
	 
	 buffer = malloc(strlen(token)*sizeof(char));  // alloca memoria per ogni token
 
	 risposte[i] = strcpy(buffer, token);   // memorizza ogni token nel vettore
      }
      
      if (strlen(token) == 0) risposte[i] = NULL;

      printf("risposte[%d ] =  %s \n", i,  risposte[i]);  // stampa di prova

      token = strtok(NULL, "#");  // continua a dividere la copia di asnw
   }   

   return risposte;   
}
Qualche idea? Oppure non ne esco e devo trovare qualche alternativa all'uso di strtok?

Avatar utente
RedSkull92
Linux 3.x
Linux 3.x
Messaggi: 567
Iscritto il: mar 21 apr 2009, 17:25
Slackware: 64bit -current
Kernel: 3.5.4
Desktop: FluxBox
Località: Palermo
Contatta:

Re: [C] Problema con strtok

Messaggio da RedSkull92 »

nelle malloc non dovresti fare il cast ?

elvis
Linux 0.x
Linux 0.x
Messaggi: 53
Iscritto il: lun 27 set 2010, 10:35
Slackware: 13.1 x64
Desktop: KDE 4.5.2
Distribuzione: Opensuse 11.3 x86

Re: [C] Problema con strtok

Messaggio da elvis »

Che io sappia con malloc il casting in C è automatico, nel caso di tipi standard, mentre è consigliato nel caso di tipi derivati (per esempio se si alloca per una struttura o una lista). In ogni caso non penso sia questo la fonte del problema, o mi sbaglio? Pensi che possa influire?

Avatar utente
targzeta
Iper Master
Iper Master
Messaggi: 6629
Iscritto il: gio 3 nov 2005, 14:05
Nome Cognome: Emanuele Tomasi
Slackware: 64-current
Kernel: latest stable
Desktop: IceWM
Località: Carpignano Sal. (LE) <-> Pisa

Re: [C] Problema con strtok

Messaggio da targzeta »

Beh, direi che il man non ti lascia scampo, con strtok(3) non si può fare. Eccoti un'alternativa:

Codice: Seleziona tutto

#include <stdio.h>  /* printf() */
#include <string.h> /* strlen(), strncpy(), etc.. */
#include <stdlib.h> /* malloc(), realloc() and free() */

char **array_answer(char *, int *);

int main()
{
  char **result;
  int num_result, i;

  result = array_answer("#casa#nonna##pollo", &num_result);

  for ( i = 0; i < num_result; i++ )
    printf("%s\n", ( result[i] ) ? result[i] : "NULL");

  /* Free allocated memory */
  for ( i = 0; i < num_result; i++ )
    if ( result[i] )
      free(result[i]);
  free(result);

  return 0;
}

char **array_answer(char *answ, int *founds)
{
  char **risposte, *tmp;
  int risp_alloc, i;

  risp_alloc = 2; /* dimensione iniziale dell'array risposte */
  risposte = malloc(risp_alloc * sizeof(char *));

  for ( *founds = 0, tmp = answ; tmp <= answ + strlen(answ); tmp += i+1, (*founds)++ )
    {
      i = strcspn(tmp, "#");

      if ( *founds == risp_alloc )
        risposte = realloc(risposte, (risp_alloc *= 2) * sizeof(char *));

      if ( i > 0 )
        {
          risposte[*founds] = malloc(i + 1);
          strncpy(risposte[*founds], tmp, i);
          risposte[*founds + 1] = '\0';
        }
      else
        risposte[*founds] = NULL;
    }

  return risposte;
}
Alcune note:
  • l'allocazione dell'array risposte è dinamica e incrementale, io sono partito da un minimo di 2, ma è meglio partire da un valore più ragionevole per minimizzare le realloc(3). Ho notato che nel tuo prototipo usi il valore N, però in linea teorica non si sa quanto quando sarà questo N.
  • La funzione tiene ovviamente conto anche dei delimitatori inseriti all'inizio e alla fine, con un po' di fantasia si può facilmente evitare questi due casi.
  • Dato che il delimitatore è un singolo carattere, forse sarebbe meglio utilizzare un algoritmo che sfrutti questo fatto, piuttosto che utilizzare una funzione come strcspn(3) che cerca stringhe. Magari si può sfruttare la funzione index(3).
  • Se al punto precedente si vuole modificare il delimitatore per farlo diventare qualcosa di più grande che di un singolo byte, allora nel for, il 'tmp += i+1' diventa 'tmp += i+strlen(delimitatore)',
  • Vale la pena notare anche il no memory leak, grazie alle free(3) nel main.
  • Ovviamente le s.c. andrebbero controllate (vedi malloc e realloc)
Emanuele
Se pensi di essere troppo piccolo per fare la differenza, prova a dormire con una zanzara -- Dalai Lama

elvis
Linux 0.x
Linux 0.x
Messaggi: 53
Iscritto il: lun 27 set 2010, 10:35
Slackware: 13.1 x64
Desktop: KDE 4.5.2
Distribuzione: Opensuse 11.3 x86

Re: [C] Problema con strtok

Messaggio da elvis »

Ti ringrazio, appena ho una mezz'ora libera mi ci metto.

elvis
Linux 0.x
Linux 0.x
Messaggi: 53
Iscritto il: lun 27 set 2010, 10:35
Slackware: 13.1 x64
Desktop: KDE 4.5.2
Distribuzione: Opensuse 11.3 x86

Re: [C] Problema con strtok

Messaggio da elvis »

spina ha scritto:Alcune note:
  • l'allocazione dell'array risposte è dinamica e incrementale, io sono partito da un minimo di 2, ma è meglio partire da un valore più ragionevole per minimizzare le realloc(3). Ho notato che nel tuo prototipo usi il valore N, però in linea teorica non si sa quanto quando sarà questo N.
  • La funzione tiene ovviamente conto anche dei delimitatori inseriti all'inizio e alla fine, con un po' di fantasia si può facilmente evitare questi due casi.
  • Dato che il delimitatore è un singolo carattere, forse sarebbe meglio utilizzare un algoritmo che sfrutti questo fatto, piuttosto che utilizzare una funzione come strcspn(3) che cerca stringhe. Magari si può sfruttare la funzione index(3).
  • Se al punto precedente si vuole modificare il delimitatore per farlo diventare qualcosa di più grande che di un singolo byte, allora nel for, il 'tmp += i+1' diventa 'tmp += i+strlen(delimitatore)',
  • Vale la pena notare anche il no memory leak, grazie alle free(3) nel main.
  • Ovviamente le s.c. andrebbero controllate (vedi malloc e realloc)
Emanuele
Allora, la funzione che mi hai suggerito va bene, però ho alcune perplessità. Preciso innanzitutto che la stringa da spezzare non ha il delimitatore iniziale # come carattere più a sinistra, mentre mi pare di aver capito che tu pensassi che lo avesse (anche se ripeto, non ci sono problemi nell'esecuzione del programma).

-Detto ciò, la dimensione del vettore di stringhe è sicuramente N, che mi è noto, per cui le realloc sono proprio necessarie?
-Anche quel *founds mi lascia qualche dubbio, praticamente tu lo inizializzi a 0 e lo incrementi fino a quando tmp non punta all'ultimo carattere di answ; però se non sbaglio con questa inizializzazione si va a sovrascrivere il valore di N nello stack della funzione chiamante (poichè chiamo la funzione array_answer con &N come secondo parametro) e se per qualche malaugurato inconveniente il for principale dovesse interrompersi (per esempio, un allocazione fallita) perderei anche il valore N stesso.
-La funzione index sarebbe...? Non riesco a trovarla da nessuna parte..
-strcspn non restituisce un puntatore a char? Come mai il compilatore non dà errore se la chiamo come hai fatto tu (cioè come se fosse una funzione void che non ritorna nulla) ?

Comunque ti ringrazio di nuovo!

Avatar utente
targzeta
Iper Master
Iper Master
Messaggi: 6629
Iscritto il: gio 3 nov 2005, 14:05
Nome Cognome: Emanuele Tomasi
Slackware: 64-current
Kernel: latest stable
Desktop: IceWM
Località: Carpignano Sal. (LE) <-> Pisa

Re: [C] Problema con strtok

Messaggio da targzeta »

elvis ha scritto:-Detto ciò, la dimensione del vettore di stringhe è sicuramente N, che mi è noto, per cui le realloc sono proprio necessarie?
No, non sono assolutamente necessarie, anzi, le puoi (dovresti) evitare.
elvis ha scritto:-Anche quel *founds mi lascia qualche dubbio, praticamente tu lo inizializzi a 0 e lo incrementi fino a quando tmp non punta all'ultimo carattere di answ; però se non sbaglio con questa inizializzazione si va a sovrascrivere il valore di N nello stack della funzione chiamante (poichè chiamo la funzione array_answer con &N come secondo parametro) e se per qualche malaugurato inconveniente il for principale dovesse interrompersi (per esempio, un allocazione fallita) perderei anche il valore N stesso.
Il prototipo della funzione vuole un puntatore ad intero, vuol dire che quando modifico *founds, io modifico il contenuto della locazione di memoria a cui punta il parametro che mi è stato passato. In pratica quello che mi viene passato non ha nessuna importanza, per me quello non è N.
elvis ha scritto:-La funzione index sarebbe...? Non riesco a trovarla da nessuna parte..
man 3 index

Codice: Seleziona tutto

 char *index(const char *s, int c);
       The  index()  function  returns a pointer to the first occurrence of the
       character c in the string s.
elvis ha scritto:-strcspn non restituisce un puntatore a char? Come mai il compilatore non dà errore se la chiamo come hai fatto tu (cioè come se fosse una funzione void che non ritorna nulla) ?
man 3 strcspn

Codice: Seleziona tutto

size_t strspn(const char *s, const char *accept);
come vedi non ritorna un puntatore a carattere.

Emanuele
Se pensi di essere troppo piccolo per fare la differenza, prova a dormire con una zanzara -- Dalai Lama

elvis
Linux 0.x
Linux 0.x
Messaggi: 53
Iscritto il: lun 27 set 2010, 10:35
Slackware: 13.1 x64
Desktop: KDE 4.5.2
Distribuzione: Opensuse 11.3 x86

Re: [C] Problema con strtok

Messaggio da elvis »

spina ha scritto:
man 3 index

Codice: Seleziona tutto

 char *index(const char *s, int c);
       The  index()  function  returns a pointer to the first occurrence of the
       character c in the string s.
Ok capito, posso chiederti dove la trovo questa funzione? Non c'è nè sul Deitel nè sul Kernighan nè la trovo in rete : D
man 3 strcspn

Codice: Seleziona tutto

size_t strspn(const char *s, const char *accept);
come vedi non ritorna un puntatore a carattere.
Perdonami, non intendevo strcspn bensì strncpy, che ritorna il puntatore alla stringa contenente la copia appena effettuata, ma a quanto pare si può chiamare come se fosse una f. che ritorna void, giusto?

Ultimissima curiosità, posso chiederti cosa intendi con man 3? Ho visto poi che questo 3 lo metti anche nel corpo delle funzioni (esempio, malloc(3), ecc.).

Mario Vanoni
Iper Master
Iper Master
Messaggi: 3174
Iscritto il: lun 3 set 2007, 21:20
Nome Cognome: Mario Vanoni
Slackware: 12.2
Kernel: 3.0.4 statico
Desktop: fluxbox/seamonkey
Località: Cuasso al Monte (VA)

Re: [C] Problema con strtok

Messaggio da Mario Vanoni »

elvis ha scritto: Ultimissima curiosità, posso chiederti cosa intendi con man 3? Ho visto poi che questo 3 lo metti anche nel corpo delle funzioni (esempio, malloc(3), ecc.).
Il mondo UNIX ha 8 capitoli di manuale,
1, 2, 3, 4, 5, 6, 7 e 8, suddivisi in campi di interesse.

P.e esiste un kill(1) come pure un kill(2),
il primo per utenti, il secondo per programmazione a livello HW.

Mettendo man 1 kill oppure man 2 kill trovi subito quello cercato.

Non so come hai impostato il tuo man(1), se digito
man 3 index
mi ritorna

Codice: Seleziona tutto

INDEX(3)                                                                           Linux Programmer's Manual                                                                          INDEX(3)



NAME
       index, rindex - locate character in string

SYNOPSIS
       #include <strings.h>

       char *index(const char *s, int c);

       char *rindex(const char *s, int c);

DESCRIPTION
       The index() function returns a pointer to the first occurrence of the character c in the string s.

       The rindex() function returns a pointer to the last occurrence of the character c in the string s.

       The terminating NULL character is considered to be a part of the strings.

RETURN VALUE
       The index() and rindex() functions return a pointer to the matched character or NULL if the character is not found.

CONFORMING TO
       4.3BSD; marked as LEGACY in POSIX.1-2001.  POSIX.1-2008 removes the specifications of index() and rindex().

SEE ALSO
       memchr(3), strchr(3), strpbrk(3), strrchr(3), strsep(3), strspn(3), strstr(3), strtok(3)

COLOPHON
       This  page  is part of release 3.15 of the Linux man-pages project.  A description of the project, and information about reporting bugs, can be found at http://www.kernel.org/doc/man-
       pages/.



GNU                                                                                       2008-08-06                                                                                  INDEX(3)

EDIT: vedi anche man 7 man-pages

Avatar utente
targzeta
Iper Master
Iper Master
Messaggi: 6629
Iscritto il: gio 3 nov 2005, 14:05
Nome Cognome: Emanuele Tomasi
Slackware: 64-current
Kernel: latest stable
Desktop: IceWM
Località: Carpignano Sal. (LE) <-> Pisa

Re: [C] Problema con strtok

Messaggio da targzeta »

elvis ha scritto:
man 3 strcspn

Codice: Seleziona tutto

size_t strspn(const char *s, const char *accept);
come vedi non ritorna un puntatore a carattere.
Perdonami, non intendevo strcspn bensì strncpy, che ritorna il puntatore alla stringa contenente la copia appena effettuata, ma a quanto pare si può chiamare come se fosse una f. che ritorna void, giusto?
Semplicemente io ignoro il valore di ritorno, che in questo caso è lo stesso di quello che gli passo io. Non la sto invocando come se fosse una funzione void, sto solo ignorando il valore di ritorno.

Per il resto ti ha risposto Mario,
Emanuele

P.S.
Mario Vanoni ha scritto:...
P.e esiste un kill(1) come pure un kill(2),
il primo per utenti, il secondo per programmazione a livello HW.
Programmazione a livello HW?
Se pensi di essere troppo piccolo per fare la differenza, prova a dormire con una zanzara -- Dalai Lama

Mario Vanoni
Iper Master
Iper Master
Messaggi: 3174
Iscritto il: lun 3 set 2007, 21:20
Nome Cognome: Mario Vanoni
Slackware: 12.2
Kernel: 3.0.4 statico
Desktop: fluxbox/seamonkey
Località: Cuasso al Monte (VA)

Re: [C] Problema con strtok

Messaggio da Mario Vanoni »

spina ha scritto: P.S.
Mario Vanoni ha scritto:...
P.e esiste un kill(1) come pure un kill(2),
il primo per utenti, il secondo per programmazione a livello HW.
Programmazione a livello HW?
Sorry, falsa interpretazione di

Codice: Seleziona tutto

 2 System calls
                 Those functions which must be performed by the kernel.
Ai tempi, essendo i kernel proprietari, agivano direttamente sulla HW.

elvis
Linux 0.x
Linux 0.x
Messaggi: 53
Iscritto il: lun 27 set 2010, 10:35
Slackware: 13.1 x64
Desktop: KDE 4.5.2
Distribuzione: Opensuse 11.3 x86

Re: [C] Problema con strtok

Messaggio da elvis »

@ Mario: perfetto, grazie. Mi si è aperto un mondo : D

@ spina: chiaro, non ci avevo proprio pensato. Grazie di tutto ancora.

Rispondi