Domanda

ho scritto un semplice parser URL utilizzando strtok (). ecco il codice

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

typedef struct {
    char *protocol;
    char *host;
    int port;
    char *path;
} aUrl;


void parse_url(char *url, aUrl *ret) {

    printf("Parsing %s\n", url);
    char *tmp = (char *)_strdup(url);
    //char *protocol, *host, *port, *path;
    int len = 0;

    // protocol agora eh por exemplo http: ou https:
    ret->protocol = (char *) strtok(tmp, "/");
    len = strlen(ret->protocol) + 2;

    ret->host = (char *) strtok(NULL, "/");


    len += strlen(ret->host);

    //printf("char at %d => %c", len, url[len]);

    ret->path = (char *)_strdup(&url[len]);

    ret->path = (char *) strtok(ret->path, "#");

    ret->protocol = (char *) strtok(ret->protocol, ":");

    // host agora é por exemplo address.com:8080
    //tmp = (char *)_strdup(host);
    //strtok(tmp, ":");
    ret->host = (char *) strtok(ret->host, ":");
    tmp = (char *) strtok(NULL, ":");

    if(tmp == NULL) {
        if(strcmp(ret->protocol, "http") == 0) {
            ret->port = 80;
        } else if(strcmp(ret->protocol, "https") == 0) {
            ret->port = 443;
        }
    } else {
        ret->port = atoi(tmp);
    }


    //host = (char *) strtok(NULL, "/");




}

/*
 * 
 */
int main(int argc, char** argv) {
    printf("hello moto\n");

    aUrl myUrl;
    parse_url("http://teste.com/Teste/asdf#coisa", &myUrl);


    printf("protocol is %s\nhost is %s\nport is %d\npath is %s\n", myUrl.protocol, myUrl.host, myUrl.port, myUrl.path);

    return (EXIT_SUCCESS);
}

Come si può vedere, io uso strtok () molto quindi posso "fetta" l'url. Non ho bisogno di supportare URL diversi da http o https in modo che il modo in cui è fatto risolve tutti i miei problemi. La mia preoccupazione è (questo è in esecuzione su un dispositivo embedded) - Sto sprecando memoria? Quando scrivo qualcosa come

ret->protocol = (char *) strtok(tmp, "/");

E poi chiamare

ret->protocol = (char *) strtok(ret->protocol, ":");

Il primo puntatore me RET-> protocollo tenuto rimangono in memoria? Ho pensato che forse avrei dovuto impostare la prima chiamata a un puntatore tmp, chiamare strtok indicando RET-> protocollo alla parte destra della stringa (la seconda chiamata) e poi libera (tmp).

Quale dovrebbe essere il modo migliore per utilizzare strtok?

È stato utile?

Soluzione

Per rispondere direttamente alla tua domanda, strtok restituisce solo un puntatore ad una posizione all'interno della stringa si dà come input-- che non alloca nuova memoria per voi, quindi non dovreste aver bisogno di chiamare gratuitamente su uno qualsiasi dei puntatori ti dà in cambio.

Per quello che vale, si potrebbe anche prendere in considerazione "strchr" e "strstr", che sono modi non distruttivi di ricerca di singoli caratteri o sequenze all'interno di stringhe.

Si noti inoltre che la vostra allocazione di memoria è problematico qui-- stai usando strdup () per allocare una nuova stringa all'interno vostra funzione di analisi, e quindi si sta assegnando frammenti di quel blocco di memoria a campi di "ret". Il chiamante sarà quindi responsabile per free'ing la stringa strdup'd, ma dal momento che si sta solo passando la stringa di nuovo implicitamente all'interno ret, il chiamante ha bisogno di sapere che cosa magicamente puntatore per passare alla libera. (Probabilmente RET-> protocollo, ma forse no, a seconda di come si presenta all'ingresso.)

Altri suggerimenti

strtok modifica la stringa sul posto, sostituendo i caratteri specificati con NULL. Dal momento che le stringhe in C sono NULL-terminati, ora sembra che il puntatore originale punta a una stringa più breve, anche se la stringa originale è ancora lì e ancora occupa la stessa quantità di memoria (ma con caratteri sostituiti con NULL). La fine della stringa, credo, contiene un doppio NULL.

La risposta è questa: Tenere un puntatore all'inizio del buffer di stringa, e hanno un altro puntatore che è il puntatore "corrente" nella stringa, come si analizzarlo. Quando si utilizza strtok o iterazioni sulla stringa in altri modi si aggiorna il puntatore "corrente", ma lascia il puntatore a partire da solo. Quando hai finito, free () il puntatore inizio. Nessuna memoria trapelata.

Sai è possibile continuare parsing della stringa utilizzando NULL come primo parametro di strtok?

Per prima chiamata:

char* token = strtok(string, delimiters);

Quindi:

token = strtok(NULL, other_delimiters);

Questo permette di semplificare il codice:

int parse_url(char *url, aUrl *ret)
{
//get protocol
char* token = strtok(url, "/");
if( token == NULL )
   return -1;
strcpy(ret->protocol, token);
strcat(ret->protocol, "//");

// skip next '/'
token = strtok(NULL, "/");
if( token == NULL )
   return -1;

//get host
token = strtok(NULL, "/");
if( token == NULL )
   return -1;
strcpy(ret->host, token);

// get path
token = strtok(NULL, "#");
if( token == NULL )
   return -1;
strcpy(ret->path, token);

// ...

return 0;
}

Si può vedere che ho avuto un valore di ritorno di sapere se l'analisi è stata effettuata con successo.

Grazie per condividere il codice! Ho eseguito all'interno valgrind e fissato due perdite di memoria generati dalle funzioni strdup.

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

typedef struct {
    char *protocol;
    char *host;
    int port;
    char *path;
} URL;

void parse_url(char *url, URL *ret) {
    char *tmp = (char *) strdup(url);
    int len = 0;

    ret->protocol = (char *) strtok(tmp, "/");
    len = strlen(ret->protocol) + 2;
    ret->host = (char *) strtok(NULL, "/");
    len += strlen(ret->host);
    ret->path = (char *) strdup(&url[len]);
    ret->path = (char *) strtok(ret->path, "#");
    ret->protocol = (char *) strtok(ret->protocol, ":");
    ret->host = (char *) strtok(ret->host, ":");
    tmp = (char *) strtok(NULL, ":");

    if (tmp == NULL) {
        if (strcmp(ret->protocol, "http") == 0) {
            ret->port = 80;
        } else if (strcmp(ret->protocol, "https") == 0) {
            ret->port = 443;
        }
    } else {
        ret->port = atoi(tmp);
    }

}

void free_url(URL *url) {
    free(url->path);
    free(url->protocol);
}

int main(int argc, char** argv) {
    URL url;
    parse_url("http://example.com:3000/Teste/asdf#coisa", &url);
    printf("protocol: %s\nhost: %s\nport: %d\npath: %s\n", url.protocol, url.host, url.port, url.path);
    free_url(&url);

    return (EXIT_SUCCESS);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top