2017-11-27 13 views
-1

Im은 C 프로그래밍을 배우기 시작하고 Im은 문자열을 부분 문자열 목록으로 분할하려고합니다. String이라는 문자열을 만드는 구조를 만들었습니다.c에서 문자열 목록 만들기가 작동하지 않습니까?

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

typedef struct dynamic_string { 
    char* data; 
    size_t capacity; 
    size_t size; 
} String; 
void free_string(String* s) { free(s->data); s->data = NULL; } 
void print_string(String* s) { for (int i = 0; i < s->size; ++i) { printf("%c", s->data[i]); } printf("\n"); } 
void clear_string(String* s) { s->size = 0; s->data[0] = '\0'; } 

int string_init(String* s, size_t init_capacity) { 
    s->data = malloc(init_capacity); 
    if (!s->data) { return -1; }; 
    s->size = 0; s->capacity = init_capacity; 
    return 0; 
} 

void add_char(String* s, char element) { 
    size_t new_size = s->size + 1; 
    if (new_size > s->capacity) { s->capacity = s->capacity << 1; s->data = realloc(s->data, s->capacity); } 
    s->data[s->size] = element; 
    s->size = new_size; 
} 

void add_charp(String* s, char* string) { 
    size_t stringlen = strlen(string); 
    size_t new_size = s->size + stringlen; 
    if (new_size > s->capacity) { s->capacity = new_size << 1; s->data = realloc(s->data, s->capacity); } 
    strcpy(s->data + s->size, string); 
    s->size = new_size; 
} 

void add_string(String* s, String* string) { 
    size_t stringlen = string->size; 
    size_t new_size = s->size + stringlen; 
    if (new_size > s->capacity) { s->capacity = new_size << 1; s->data = realloc(s->data, s->capacity); } 
    strcpy(s->data + s->size, string->data); 
    s->size = new_size; 
} 

이제는 문제가되지 않습니다. 이러한 문자열 목록을 만들려고 할 때 문제가 발생합니다. 나는 또한 이것을하기위한 구조를 만들었다.

typedef struct dynamic_string_pointer { 
    String* data; 
    size_t capacity; 
    size_t size; 
} StringVec; 
void free_stringvec(StringVec* sv) { free(sv->data); sv->data = NULL; } 
void print_stringvec(StringVec* sv) { for (int i = 0; i < sv->size; ++i) { print_string(&sv->data[i]); } } 

int stringvec_init(StringVec* sv, size_t init_capacity) { 
    sv->data = malloc(init_capacity * sizeof(String)); 
    if (!sv->data) { return -1; } 
    sv->size = 0; sv->capacity = init_capacity; 
    return 0; 
} 

void add_string_to_vec(StringVec* sv, String string) { 
    size_t new_size = sv->size + 1; 
    if (new_size > sv->capacity) { sv->capacity = sv->capacity << 1; sv->data = realloc(sv->data, sv->capacity * sizeof(String)); } 
    sv->data[sv->size] = string; 
    sv->size = new_size; 
} 

이 구조체에는 문제가없는 것 같으며 다른 문자열을 추가하면 올바르게 작동합니다. 문제는 내가 문자열을 만들고 동일한 변수를 사용하여 목록에 추가 할 때입니다. 예를 들어

: 나는 변수 토큰을 사용하여 토큰에 추가하여 토큰의 목록을 구축을 위해 노력하고

void tokenize(char* text) { 
    StringVec tokens; stringvec_init(&tokens, 32); 
    String token; string_init(&token, 8); 
    bool first_char = false; 

    for (int i = 0; i < strlen(text); ++i) { 
     if (isToken(text[i])) { 
      if (!equals_charp(&token, "")) { add_string_to_vec(&tokens, token); } 
      clear_string(&token); add_char(&token, text[i]); 
      add_string_to_vec(&tokens, token); 
      clear_string(&token); first_char = false; continue; 
     } 
     if (text[i] != ' ' && text[i] != '\t' && text[i] != '\n'){ add_char(&token, text[i]); first_char = true; } 
     else if (first_char) { add_char(&token, text[i]); } 
    } 
    print_stringvec(&tokens); 

    free_string(&token); 
    free_stringvec(&tokens); 
} 

여기. 문제는 토큰이 add_string_to_vec에 전달 될 때 항상 스택에 동일한 주소를 갖고있는 것으로 보입니다. 이것은 토큰을 인쇄 할 때 단지 마지막 토큰을 반복해서 인쇄하기 때문에 옳은 것처럼 보입니다. 그래서 나는이> 데이터를 SV-하는 문자열에서 메모리를 복사 할 것이라고 생각

sv->data[sv->size] = string; with memcpy(sv->data + sv->size, &string, sizeof(String));

교체 시도 있도록 일하는 것이 추가 할 때 다른 토큰. 이것은 같은 문제가있는 것 같습니다. 나는 보통 C++에서 std :: vector와 std :: string을 사용하여 쉽게 작업 할 수있다. 나는 어리석은가 아니면 어쩌면 기억이 어떻게 잘 작동 하는지를 이해할 수도 있지만, 이것이 작동하도록하는 방법을 찾아 내지 못했습니다.

+0

'SV-> = 데이터의 malloc (init_capacity의 *를는 sizeof (문자열)) (! equals_charp (토큰 ""))'이상한 ... 또한''경우 매우 * * 이상한 ... – wildplasser

+0

BTW 'add_charp()'에서'new_size == s-> capacity'이라면'strcpy()'는 최종 '\ 0'을 소유하지 않은 메모리에 씁니다. –

+0

'string_init'을 한 번만 호출하십시오. 그러면 하나의 버퍼가 할당되고,이 버퍼는'token' 문자열의 모든 복사본간에 공유됩니다. 'String'을 복사 할 때마다 새로운 버퍼를 할당하고 오래된'data' 문자를 복사해야합니다. (그러면 '자유'라고 부를 때 알아내는 데 어려움이 많을 것입니다.) –

답변

0

나는 보 페르손 (Bo Persson)이 말한 것을 수행하여 이러한 기능을 만들었습니다. 이제 완벽하게 작동합니다. (토큰 화 기능이 약간 작동하는 방식을 변경했습니다.)

String next_token(char* text, size_t size, int* start) { 
    String token; string_init(&token, 4); 

    char last_char = ' '; 
    for (int i = *start; i < size; ++i) { 
     if (last_char == ' ' && text[i] == ' ') { continue; } 
     if (text[i] == '\t' || text[i] == '\n') { if (last_char != ' ') { add_char(&token, ' '); } continue;} 
     if (isToken(text[i])) { 
      *start = i; 
      if (token.size == 0) { *start += 1; add_char(&token, text[i]); } 
      return token; 
     } 
     add_char(&token, text[i]); 
     last_char = text[i]; 
    } 

    return token; 
} 

StringVec tokenize(char* text) { 
    StringVec tokens; stringvec_init(&tokens, 32); 

    size_t textlen = strlen(text); 

    for (int i = 0; i < textlen;) { 
     String token = next_token(text, textlen, &i); 
     add_string_to_vec(&tokens, token); 
    } 

    return tokens; 
} 

여기 무료 토큰입니다.

void free_stringvec(StringVec* sv) { 
    for (int i = 0; i < sv->size; ++i) { 
     free_string(&sv->data[i]); 
    } 
    free(sv->data); sv->data = NULL; 
}