Вопрос

Я написал простой анализатор URL-адресов, используя strtok().вот код

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

Как видите, я часто использую strtok(), чтобы «разрезать» URL-адрес.Мне не нужно поддерживать URL-адреса, отличные от http или https, поэтому способ, которым это сделано, решает все мои проблемы.Меня беспокоит (это работает на встроенном устройстве): я трачу память?Когда я пишу что-то вроде

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

А потом позвони позже

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

Остается ли в памяти первый указатель ret->protocol?Я подумал, что, возможно, мне следует установить первый вызов на указатель tmp, вызвать strtok, указывающий ret->protocol на правую часть строки (второй вызов), а затем free(tmp).

Каким должен быть лучший способ использовать strtok?

Это было полезно?

Решение

Чтобы ответить на ваш вопрос напрямую, strtok возвращает только указатель на место внутри строки, которую вы даете ему в качестве входных данных, - он не выделяет для вас новую память, поэтому не нужно вызывать free для любого из указателей, которые он вам дает. обратно взамен.

Если это того стоит, вы также можете изучить «strchr» и «strstr», которые представляют собой неразрушающие способы поиска отдельных символов или последовательностей внутри строк.

Также обратите внимание, что выделение памяти здесь проблематично: вы используете strdup() для выделения новой строки внутри функции синтаксического анализа, а затем назначаете фрагменты этого блока памяти полям «ret».Таким образом, ваш вызывающий объект будет нести ответственность за освобождение строки strdup'd, но поскольку вы передаете эту строку обратно только неявно внутри ret, вызывающий объект должен волшебным образом знать, какой указатель передать в free.(Вероятно, ret->protocol, а может и нет, в зависимости от того, как выглядят входные данные.)

Другие советы

strtok изменяет строку на месте, заменяя указанные символы NULL.Поскольку строки в C оканчиваются NULL, теперь кажется, что ваш исходный указатель указывает на более короткую строку, хотя исходная строка все еще существует и занимает тот же объем памяти (но символы заменены на NULL).Я думаю, что конец строки содержит двойной NULL.

Краткий ответ таков:Сохраните указатель на начало строкового буфера и используйте еще один указатель, который является вашим «текущим» указателем на строку при ее анализе.Когда вы используете strtok или перебираете строку другими способами, вы обновляете «текущий» указатель, но оставляете начальный указатель в покое.Когда вы закончите, free() — начальный указатель.Память не утекла.

Знаете ли вы, что можете продолжить анализ строки, используя NULL в качестве первого параметра strtok?

Первый звонок:

char* token = strtok(string, delimiters);

Затем:

token = strtok(NULL, other_delimiters);

Это позволит вам упростить код:

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

Вы можете видеть, что у меня было возвращаемое значение, чтобы узнать, был ли синтаксический анализ успешно выполнен.

Спасибо, что поделились своим кодом!Я запустил его внутри valgrind и исправил две утечки памяти, вызванные функциями 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);
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top