2016-12-22 5 views
2

이것은 결코 끝나지 않을 문제입니다. 이 작업은 웹 서버에서 요청 라인을 구문 분석하는 것입니다. - 불확실한 길이의 C. - C에서 다음과 같이 웹에서 다음과 같은 작업을 수행합니다. /path/script.cgi 쿼리 : ?field1=value1&field2=value2C에서 HTTP 요청 라인 구문 분석

GET /path/script.cgi?field1=value1&field2=value2 HTTP/1.1 

나는 절대 경로를 추출해야합니다. 다음 기능이 키를 보유하고 있다고 들었습니다 : strchr, strcpy, strncmp, strncpy 및/또는 strstr.

지금까지 무슨 일이 있었는지 : strchrstrstr과 같은 기능을 사용하면 특정 시점에 요청 줄을 자르도록 허용하지만 결코 요청 줄의 일부를 제거하지 않을 것임을 배웠습니다. 원하지 않아요. 어떻게 레이어링하든 상관 없습니다.

예를 들어, 다음은 쿼리 분리에 가까운 코드이지만 http 버전을 제거 할 수 없습니다.

bool parse(const char* line) 
{ 
    // request line w/o method 
    const char ch = '/'; 
    char* lineptr = strchr(line, ch); 

    // request line w/ query and HTTP version 
    char ch_1 = '?'; 
    char* lineptr_1 = strchr(lineptr, ch_1); 

    // request line w/o query 
    char ch_2 = ' '; 
    char* lineptr_2 = strchr(lineptr_1, ch_2); 

    printf("%s\n", lineptr_2); 

    if (lineptr_2 != NULL) 
     return true; 
    else 
     return false; 
} 

없이 내가 절대 경로를 분리하려고 비슷한 문제를 가지고 말 (나는 방법을 도랑 아닌? 또는 아무것도 이후 수), 나는 어떤 기회가있는 나는 기능을 사용할 수 없습니다 참조 그게 나를 알 필요가 선험적으로 하나의 위치 (일반적으로 배열)에서 다른 곳으로 복사하고 싶습니다 얼마나 많은 문자가 실시간으로 실행될 때, 나는 요청 줄이 어떻게 생겼는지 전혀 모른다. 미리. 누군가 내가 누락 된 것을보고 올바른 방향으로 나를 가리킬 수 있다면, 나는 가장 감사 할 것입니다!

+0

심볼, 공백 또는 개행까지 구문 분석 할 수있는 함수를 직접 작성하지 않으시겠습니까? 그런 다음이 함수를 사용하여 절대 경로 (첫 번째 '?'또는 '\ n'또는 ''까지 구문 분석)를 사용하고 같은 함수를 다시 사용할 수 있습니다. 시작 인덱스는 절대 문자의 마지막 문자 인덱스입니다. 경로 더하기 1을 사용하여 쿼리 문자열을 가져옵니다. –

+0

좋은 생각입니다. 그렇게해야 할 수도 있습니다. 물론, 내가 마지막으로 시도했을 때 나는 strcpy를 재발 명했다. lol – Ryan

+1

프로그래밍의 아름다움, 특히 C와 같은 저수준 언어에서의 아름다움. 필요한 것이 있으면 존재하지 않는다면 그것을 만들 수 있습니다! –

답변

2

더 멋진 해결책.

#include <stdio.h> 
#include <string.h> 

int parse(const char* line) 
{ 
    /* Find out where everything is */ 
    const char *start_of_path = strchr(line, ' ') + 1; 
    const char *start_of_query = strchr(start_of_path, '?'); 
    const char *end_of_query = strchr(start_of_query, ' '); 

    /* Get the right amount of memory */ 
    char path[start_of_query - start_of_path]; 
    char query[end_of_query - start_of_query]; 

    /* Copy the strings into our memory */ 
    strncpy(path, start_of_path, start_of_query - start_of_path); 
    strncpy(query, start_of_query, end_of_query - start_of_query); 

    /* Null terminators (because strncpy does not provide them) */ 
    path[sizeof(path)] = 0; 
    query[sizeof(query)] = 0; 

    /*Print */ 
    printf("%s\n", query, sizeof(query)); 
    printf("%s\n", path, sizeof(path)); 
} 

int main(void) 
{ 
    parse("GET /path/script.cgi?field1=value1&field2=value2 HTTP/1.1"); 
    return 0; 
} 
+0

흥미 롭습니다. 여기에 풀어야 할 것이 많습니다. 포인터 빼기를 수행하는 것 같습니다. 나는 당신이 C에서 그것을 할 수 있다는 것을 몰랐습니다. char 배열 길이가 60이라고 가정해야합니까? 아니면 내가 모르는 국제 대회가 있습니까? – Ryan

+1

나는 나보다 많은 논평을 추가해야했다. 60은 임의적이었다. 포인터 뺄셈을 사용하여 실제로 올바른 양의 메모리를 얻을 수 있습니다. –

+1

이 대화에 추가하려면 C에서 포인터 연산을 확실히 수행 할 수 있습니다. 각 포인터는 본질적으로 메모리 주소이고 배열에 대한 포인터는 순차 데이터를 참조합니다. 메모리 주소에 포인터에 의해 참조되는 타입. 즉, 다른 유형의 포인터에 대해 포인터 연산을 수행하지 않도록주의하십시오. 포인터 연산이 적절하게 정렬되지 않을 수 있기 때문입니다. 컴파일러는 "1,2,3"과 같은 10 진수 값을 적절한 포인터 오프셋으로 변환 할 수 있으므로 "선 + 4"와 같은 것에 대해 걱정할 필요가 없습니다. –

2

필자는 C++의 일부 함수를 C++의 getline과 유사한 구분 기호까지 수동으로 구문 분석하는 동안 함수를 작성했습니다.

// Trims all leading whitespace along with consecutive whitespace from provided cstring into destination char*. WARNING: ensure size <= sizeof(destination) 
void Trim(char* destination, char* source, int size) 
{ 
    bool trim = true; 
    int index = 0; 
    int i; 
    for (i = 0; i < size; ++i) 
    { 
     if (source[i] == '\n' || source[i] == '\0') 
     { 
      destination[index++] = '\0'; 
      break; 
     } 
     else if (source[i] != ' ' && source[i] != '\t') 
     { 
      destination[index++] = source[i]; 
      trim = false; 
     } 
     else if (trim) 
      continue; 
     else 
     { 
      if (index > 0 && destination[index - 1] != ' ') 
       destination[index++] = ' '; 
     } 
    } 
} 

// Parses text up to the provided delimiter (or newline) into the destination char*. WARNING: ensure size <= sizeof(destination) 
void ParseUpToSymbol(char* destination, char* source, int size, char delimiter) 
{ 
    int index = 0; 
    int i; 
    for (i = 0; i < size; ++i) 
    { 
     if (source[i] != delimiter && source[i] != '\n' && source[i] != '\0' && source[i] != ' ')) 
     { 
      destination[index++] = source[i]; 
     } 
     else 
     { 
      destination[i] = '\0'; 
      break; 
     } 
    } 

    Trim(destination, destination, size); 
} 

그런 다음 당신이이 라인을 따라 뭔가로 C-문자열을 구문 분석 할 수 : 위의 코드는이 경우, 대상 문자열에서 임의의 맨 앞과 맨 뒤의 공백을 트림

char* buffer = (char*)malloc(64); 
char* temp = (char*)malloc(256); 
strcpy(temp, "GET /path/script.cgi?field1=value1&field2=value2 HTTP/1.1"); 
Trim(temp, temp, 256); 
ParseUpToSymbol(buffer, cstr, 64, '?'); 
temp = temp + strlen(buffer) + 1; 
Trim(temp, temp, 256); 

"GET/경로/script.cgi? field1 = value1 & field2 = value2 HTTP/1.1 "로 변경 한 다음 구문 분석 된 값을 변수 버퍼에 저장합니다. 처음 실행하면 버퍼 안에 "GET"이라는 단어를 넣어야합니다. "temp = temp + strlen (buffer) + 1"을 수행하면 temp char 포인터를 다시 조정하므로 문자열의 나머지 부분과 함께 ParseUpToSymbol을 다시 호출 할 수 있습니다. 다시 호출 할 경우 첫 번째 물음표까지 이어지는 절대 경로를 가져야합니다. 이 작업을 반복하여 각 쿼리 문자열을 가져 오거나 구분 기호를 공백으로 변경하고 URL의 전체 쿼리 문자열 부분을 가져올 수 있습니다. 나는 당신이 생각을 가지고 있다고 생각합니다. 이것은 물론 많은 솔루션 중 하나 일뿐입니다.

+0

많은 감사! 나는 이것을 철저히 검토 할 것이다.그것은 필자가 필요로하는 것의 유형과 같을 것입니다. 왜냐하면 필자는 필연적으로 심벌이나 스페이스까지 파싱해야하기 때문입니다. 이것을 공유해 주셔서 감사합니다! – Ryan

+1

언제든지. 나는 이것이 당신을 위해 일하기를 바랍니다 - 그리고 당신이 적합하다고 생각하는대로 그것을 자유롭게 수정하십시오. (또한 어떤 오류라도 발견하면 회신하여주십시오.) 이것은 필자가 약간의 C 프로젝트에 대해 작성한 코드 중 하나입니다. 필자는 필자가 필요로하는 라이브러리 기능을 사용할 수없는 비슷한 딜레마에 빠졌습니다. sscanf()에서는 항상 흥미로운 형식 지정자를 사용할 수 있지만 때로는 자신의 루틴을 사용하고 코드를 복잡하게 만드는 것을 피하는 것이 좋습니다. –