Pregunta

escribí un analizador simple url usando strtok (). Aquí está el código

#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);
}

Como se puede ver, yo uso strtok () mucho, así que puede "rebanada" de la URL. No necesito para soportar URLs diferentes de HTTP o HTTPS por lo que la forma en que se realiza resuelve todos mis problemas. Mi preocupación es (esto se ejecuta en un dispositivo integrado) - Estoy perdiendo la memoria? Cuando escribo algo así como

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

Y luego llamar más tarde

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

¿Tiene mi primera ret- puntero> Protocolo que se quedan en la memoria? Pensé que tal vez debería establecer la primera llamada a un puntero tmp, llame strtok señalando ret-> protocolo para la parte derecha de la cadena (la segunda llamada) y luego libre (TMP).

¿Cuál debe ser la mejor manera de utilizar strtok?

¿Fue útil?

Solución

Para responder a su pregunta directamente, strtok solamente devuelve un puntero a una ubicación dentro de la cadena que da como input-- no asigna memoria nueva para usted, por lo que no debería tener que llamar gratis en cualquiera de los punteros le da a cambio.

Por lo que vale la pena, también se puede mirar en "strchr" y "strstr", que son formas no destructivas de la búsqueda de caracteres individuales o secuencias dentro de las cadenas.

Tenga en cuenta también que su asignación de memoria es problemático aquí-- que está utilizando strdup () para asignar una nueva cadena dentro de su función de análisis, y luego estás asignando fragmentos de ese bloque de memoria a los campos de "ret". El interlocutor deberá por lo tanto ser responsable de la cadena free'ing strdup'd, pero ya que sólo está pasando esa cadena hacia atrás de manera implícita en el interior ret, la persona que llama tiene que saber lo que mágicamente puntero para pasar a la libre. (Probablemente ret-> protocolo, pero tal vez no, dependiendo de cómo se ve la entrada.)

Otros consejos

strtok modifica la secuencia en el lugar, en sustitución de los caracteres especificados con NULL. Desde cadenas en C son terminada en nulo, ahora parece que el puntero original se apunta a una cadena más corta, a pesar de que la cadena original sigue ahí y sigue ocupando la misma cantidad de memoria (pero con caracteres reemplazados con NULL). El final de la cadena, creo, contiene un doble NULL.

La respuesta corta es la siguiente: Mantener un puntero al comienzo de su búfer de cadena, y tienen otro puntero que es el puntero del "actual" en la cadena a medida que analizarlo. Cuando se utiliza strtok o iterar sobre la cadena de otras maneras de actualizar el puntero "actual", pero deja el puntero a partir solo. Cuando haya terminado, libre () el puntero de inicio. No se filtró memoria.

¿Usted sabe que puede continuar con el análisis de la cadena utilizando NULL como primer parámetro de strtok?

Primera llamada:

char* token = strtok(string, delimiters);

Entonces:

token = strtok(NULL, other_delimiters);

Esto le permite simplificar el código:

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;
}

Se puede ver que tenía un valor de retorno para saber si el análisis se llevó a cabo con éxito.

Gracias por compartir su código! Me encontré dentro valgrind y fija dos pérdidas de memoria generadas por las funciones 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);
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top