2009-09-29 6 views
4

strtok()을 사용하여 간단한 URL 파서를 작성했습니다. 당신이 볼 수 있듯이 나는 여기() 많은 그래서 할 수있는 "조각"의 URL strtok를를 사용하여 코드를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); 
} 

을합니다. http 또는 https와 다른 URL을 지원할 필요가 없으므로 모든 문제가 해결됩니다. 내 관심사가 임베디드 기기에서 실행 중입니다. 메모리를 낭비하고 있습니까? 그때

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

그리고 같은 나중에 전화를 쓸 때 는

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

개최 나를 먼저 포인터 RET-> 프로토콜은 메모리에 남아 있습니까

? 아마 내가 tmp 포인터에 첫 번째 호출을 설정해야한다고 생각했는데 strtok이 문자열의 오른쪽 부분 (두 번째 호출)에 ret-> 프로토콜을 가리킨 다음 무료 (tmp)를 호출해야한다고 생각했습니다.

strtok을 사용하는 가장 좋은 방법은 무엇입니까?

+3

안전을 위해 모든 strtok() 이후에 NULL 결과가 있는지 확인해야합니다. 귀하의 '/'가 없을 경우를 대비하여 귀하는 불만을 제기 할 필요가 없습니다. 일부 린트 타입 도구는 이러한 유형의 것에 대해 불평해야합니다. 입력이 정확하다고 가정하지 마십시오. – KFro

답변

19

질문에 직접 대답하기 위해 strtok은 입력 한 문자열 안의 위치에 대한 포인터 만 반환합니다. 새 메모리를 할당하지 않으므로 strtok에서 새 메모리를 할당하지 않습니다. 다시 돌아온 포인터.

가치가있는 경우 "strchr"및 "strstr"을 살펴볼 수도 있습니다.이 방법은 문자열에서 단일 문자 또는 시퀀스를 검색하는 비파괴적인 방법입니다.

메모리 할당은 문제가 있습니다. strdup()을 사용하여 구문 분석 함수 내에 새 문자열을 할당 한 다음 해당 메모리 블록의 조각을 "ret"필드에 할당하고 있습니다. 호출자는 strdup 된 문자열을 free'ing 할 책임이 있습니다. 그러나 ret 안에 암시 적으로 그 문자열을 전달하기 때문에 호출자는 어떤 포인터를 자유롭게 전달할 것인지 마술 적으로 알아야합니다. (아마도 ret-> protocol이지만, 입력이 어떻게 보이는지에 따라 다르다.)

+1

strchr (또는 strrchr, 역순으로 처리하는 경우도 있음)에서 +1을 반복합니다. 아마도 루프를 반복합니다. 이 방법으로, 당신은 당신의 원래 문자열을 망쳐 놓지는 않겠지 만, 당신은 여전히 ​​당신이 원하는 조각을 꺼낼 수 있습니다. 이전에 strchr()을 사용한 적이없는 경우 여기를 참고하십시오 : http://www.cplusplus.com/reference/clibrary/cstring/strchr/ –

3

strtok의 첫 번째 매개 변수로 NULL을 사용하여 문자열을 계속 파싱 할 수 있다는 것을 알고 계십니까?

먼저 호출 : 다음

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

당신은 내가 구문 분석하면 알 수 반환 값을 가지고 볼 수 있습니다 성공적으로 완료되었습니다.

+0

이 유선 버전을 사용하면 "ret"구조의 char * 필드에 복사 된 문자열에 할당 된 메모리가 있는지 확인해야합니다. (여기에 쓰여진 strcat은 날아갈 것입니다.) 이 전략을 strcat없이 사용할 수도 있습니다. 처음에했던 것처럼 ret->에 직접 할당하십시오. –

5

strtok은 지정된 문자를 NULL로 바꾸어 문자열을 수정합니다. C에서 문자열은 NULL로 끝나기 때문에, 원래 문자열이 여전히 있고 여전히 같은 양의 메모리를 차지하지만 (NULL로 대체 된 문자로) 원래 포인터가 더 짧은 문자열을 가리키는 것으로 나타납니다. 문자열의 끝은 double-NULL을 포함한다고 생각합니다.

짧은 대답은 다음과 같습니다. 문자열 버퍼의 시작 부분에 대한 포인터를 유지하고 구문 분석 할 때 문자열에 대한 "현재"포인터 인 다른 포인터가 있어야합니다. 다른 방법으로 strtok을 사용하거나 문자열을 반복 할 때 "현재"포인터를 업데이트하지만 시작 포인터는 그대로 두십시오. 작업이 끝나면 시작 포인터를 놓으십시오(). 메모리가 유출되지 않았습니다.

1

코드를 공유해 주셔서 감사합니다. 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); 
} 
관련 문제