Frage

Ich schrieb einen einfachen URL-Parser mit strtok (). hier ist der Code

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

Wie Sie sehen können, verwende ich strtok () eine Menge, so kann ich „Scheibe“ die URL. Ich brauche nicht Urls zu unterstützen anders als http oder https, so wie es getan hat alle meine Probleme löst. Meine Sorge ist, (das auf einem Embedded-Gerät läuft) - vergeude ich Speicher? Wenn ich schreibe, so etwas wie

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

Und dann später anrufen

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

Was ich ersten Zeiger retro-> Protokoll im Speicher bleiben? Ich dachte, dass vielleicht sollte ich den ersten Anruf zu einem tmp Zeiger setzen, rufen Sie strtok zeigt retro-> Protokoll auf den rechten Teil der Zeichenfolge (der zweite Anruf) und dann frei (tmp).

Was soll der beste Weg sein strtok zu benutzen?

War es hilfreich?

Lösung

Ihre Frage direkt zu beantworten, gibt strtok nur einen Zeiger auf eine Stelle innerhalb der Zeichenfolge, die Sie geben als input-- es keine neue Speicher für Sie nicht vor, doch so braucht nicht auf einen des Zeigers frei rufen es gibt Ihnen wieder zurück.

Für das, was es wert ist, Sie auch in „strchr“ aussehen könnten und „strstr“, das sind die zerstörungsfreie Wege, innerhalb von Strings für einzelne Zeichen oder Sequenzen zu suchen.

Beachten Sie auch, dass Ihre Speicherzuweisung problematisch ist hier-- Sie verwenden strdup () eine neue Zeichenfolge in Ihrem Parse-Funktion zuzuweisen, und dann sind Sie Fragmente dieses Speicherblocks zu Bereichen „ret“ zuweisen. Der Anrufer wird damit verantwortlich für free'ing die strdup'd Zeichenfolge, aber da Sie nur die Zeichenfolge zurück implizit innerhalb ret geht, muss der Anrufer auf magische Weise wissen, welche Zeiger auf frei passieren. (Wahrscheinlich Ret> Protokoll, aber vielleicht auch nicht, je nachdem, wie der Eingang aussah.)

Andere Tipps

strtok ändert die Zeichenfolge im Ort, die angegebenen Zeichen mit NULL zu ersetzen. Da Strings in C NULL-termini ist, scheint es nun, dass Ihr ursprünglicher Zeiger auf eine kürzere Zeichenfolge zeigt, obwohl die ursprüngliche Zeichenfolge ist immer noch da und nimmt immer noch die gleiche Menge an Speichern (aber mit Zeichen mit NULL ersetzt). Das Ende der Schnur, glaube ich, ein Schlafzimmer mit Doppel NULL.

Die kurze Antwort lautet: einen Zeiger auf den Anfang der Zeichenfolge-Puffer halten, und einen anderen Zeiger haben, dass Ihr „aktuellen“ Zeiger in den String ist, wie Sie es analysieren. Wenn Sie strtok oder iterieren die Zeichenfolge auf andere Weise verwenden, aktualisieren Sie den „aktuellen“ Zeiger sondern den Anfang Zeiger in Ruhe lassen. Wenn Sie fertig sind, free () der Anfangszeiger. Kein Speicher verloren.

Haben Sie wissen, dass Sie auch weiterhin den String-Parsing mit NULL als ersten Parameter von strtok?

Erster Aufruf:

char* token = strtok(string, delimiters);

Dann:

token = strtok(NULL, other_delimiters);

Dies ermöglicht es Ihnen, Ihren Code zu vereinfachen:

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

Sie können sehen, ich einen Rückgabewert musste wissen, ob das Parsen erfolgreich durchgeführt wurde.

Vielen Dank für Ihre Code-Sharing! Ich lief es in valgrind und fixiert zwei Speicherlecks, die durch strdup Funktionen.

#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);
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top