Pergunta

Eu escrevi um analisador url simples usando strtok (). aqui está o 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 você pode ver, eu uso strtok () muito, então eu posso "fatia" da URL. Eu não preciso de urls de apoio diferentes de HTTP ou HTTPS para que a forma como é feito resolve todos os meus problemas. A minha preocupação é (este está sendo executado em um dispositivo embutido) - Am I memória desperdício? Quando eu escrever algo como

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

E depois chamar

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

Será que me primeiro RET ponteiro> protocolo realizada permanecem na memória? Eu pensei que talvez eu deveria definir a primeira chamada para um ponteiro tmp, chamada strtok RET> protocolo para a parte direita da string (a segunda chamada) e depois livre (tmp) apontando.

Qual deve ser a melhor maneira de usar strtok?

Foi útil?

Solução

Para responder à sua pergunta diretamente, strtok só retorna um ponteiro para uma localização dentro da cadeia que dá-la como input-- não alocar nova memória para você, então não precisa ligar gratuitamente em qualquer um dos ponteiros que lhe dá em troca.

Por que vale a pena, você também pode olhar para "strchr" e "strstr", que são formas não destrutivos de procura de caracteres individuais ou sequências dentro cordas.

Também nota que a sua alocação de memória é problemático aqui-- você estiver usando strdup () para alocar uma nova seqüência dentro de sua função de análise, e então você está atribuindo fragmentos desse bloco de memória para campos de "ret". O seu interlocutor, assim, ser responsável por free'ing a corda strdup'd, mas desde que você está passando apenas a corda para trás implicitamente dentro ret, as necessidades do chamador saber magicamente o ponteiro passar para libertar. (Provavelmente RET> protocolo, mas talvez não, dependendo de como os olhares de entrada.)

Outras dicas

modifica strtok a corda no lugar, substituindo os caracteres especificados com NULL. Desde cordas em C são terminada em NULL, agora parece que o ponteiro original apontando para uma cadeia mais curta, embora a string original ainda está lá e ainda ocupa a mesma quantidade de memória (mas com personagens substituído com NULL). O fim da cadeia, eu acho, contém um double-NULL.

A resposta curta é: Mantenha um ponteiro para o início do seu buffer de string, e ter um outro ponteiro que é o ponteiro "corrente" na string como você analisá-lo. Quando você usar strtok ou iterar sobre a string de outras formas de atualizar o ponteiro "corrente", mas deixar o ponteiro início sozinho. Quando tiver terminado, free () o ponteiro começando. Sem memória vazada.

Você sabe que você pode continuar a análise do string usando NULL como o primeiro parâmetro de strtok?

Primeira chamada:

char* token = strtok(string, delimiters);

Depois:

token = strtok(NULL, other_delimiters);

Esta permitem simplificar o seu 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;
}

Você pode ver que eu tinha um valor de retorno para saber se a análise foi feita com sucesso.

Obrigado por compartilhar o seu código! Corri-lo dentro valgrind e fixou dois vazamentos de memória gerados por funções 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top