2015-05-19 3 views
0

У меня есть этот код, который извлекает строки между двумя указанными строками, конкретно <title> и </title>. Но когда я запускаю программу, я получаю Segmentation Fault: 11 Любые решения?Ошибка сегментации в синтаксическом коде

int main(){ 

     struct stat st; 
     stat("test.txt", &st); 
     int size = st.st_size; 
     printf("%d\n", size); 
     FILE *f = fopen("test.txt", "rb"); 

     char *bytes = (char*)malloc(size);   
     fread(bytes,size,1,f); 
     fclose(f); 
     parser(bytes); 
     return 0; 
} 

void parser(char *bytes){ 
     struct stat st; 
     stat(bytes, &st); 
     int size = st.st_size; 
     char *output = (char*)malloc(size); 
     char *ptr = strstr(bytes, "<title>"); 
     char *ptr2 = strstr(ptr, "</title>"); 
     if(ptr2){ 
      strncpy(output, ptr+7, (ptr2 - (ptr+7))); 
      puts(output); 
      free(output); 
      parser(ptr2); 
     } 
     free(bytes); 
     free(output); 

} 
+2

Вы тестируете размер 'yeah.txt', но затем открываете 'test.txt'? – narb

+1

Потому что вы «статируете» содержимое файла вместо имени файла в 'parser'? Ваш char buffer 'bytes', а также' output' не заканчиваются нулем, что требуется для функций в '', например. 'Strstr'. –

+0

@narb Извините, это должно быть «test.txt» вместо «yeah.txt» –

ответ

3

Здесь немало проблем.

В порядке:

  • вы дважды освободив output (в том случае, когда вы найдете строку). Это очень вероятно спровоцирует segfault, хотя не обязательно в точке второго вызова free. Вы также освобождаете bytes, даже если это не тот адрес, который был возвращен с malloc (в рекурсивном вызове разбора). Это также вызовет проблемы, и это плохой дизайн: функции, как правило, не должны содержать свободные строки, которые передаются им в качестве аргументов.

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

  • Вы, вероятно, не хотите использовать strncpy. strncpy не завершает нулевую копию копии, если в оригинале не найден NUL. Таким образом, вы получаете неиспользованную копию, которая может вызвать всевозможные хаосы.

    Вместо этого просто используйте memcpy (который также не завершает нуль, но, по крайней мере, он не обманывает вас, думая, что он может) и добавьте NUL самостоятельно.

  • Строка, которую вы изначально читали из файла, не завершена NUL. Таким образом, strstr будет продолжать чтение за пределами строки.

  • parser является рекурсивным, а не рекурсивным. Его можно было бы легко записать как хвост рекурсивный, и ваш компилятор C мог бы применить TCO в этом случае, но, как написано, он может создать большой стек вызовов.

  • parser не проверяет, что первый strstr нашел строку перед вызовом strstr на результат. Поэтому, когда вас нет больше <title>, вы можете позвонить ptr2 = strstr(NULL, "</title>");. Это, безусловно, будет segfault.

Вот код, который может помочь:

/* Forward declare parser */ 
void parser(char *bytes); 

int main(){ 
     struct stat st; 
     stat("test.txt", &st); 
     /* CHECK RETURN VALUE */ 
     int size = st.st_size; 
     printf("%d\n", size); 
     FILE *f = fopen("test.txt", "rb"); 
     /* CHANGE: need space for the NUL */ 
     char *bytes = malloc(size + 1);   
     size_t nread = fread(bytes,size,1,f); 
     if (nread != size) { /* HANDLE ERROR */ } 
     /* CHANGE: NUL terminate string */ 
     bytes[nread] = 0; 
     fclose(f); 
     parser(bytes); 
     /* CHANGE: We allocated bytes, we free it */ 
     free(bytes); 
     return 0; 
} 

void parser(char *bytes){ 
    char *ptr = strstr(bytes, "<title>"); 
    /* CHANGE: Make sure strstr found something */ 
    if (ptr) { 
     /* Skip over the found string */ 
     ptr += 7: 
     char *ptr2 = strstr(ptr, "</title>"); 
     if (ptr2) { 
      /* Don't allocate buffer until we need it */ 
      /* Remember to leave space for the NUL */ 
      char* output = malloc(ptr2 - ptr + 1); 
      memcpy(output, ptr, ptr2 - ptr); 
      /* null-terminate */ 
      output[ptr2 - ptr] = 0; 
      puts(output); 
      free(output); 
      parser(ptr2); 
     } 
    } 
} 

Это не лучший код либо. Но это показывает некоторые из вещей, о которых вы могли подумать.

+0

Благодарим вас за код. Это действительно полезно. Однако есть одна проблема. Значение * ptr равно нулю, когда я запускаю код. –

+0

И когда я пытаюсь напечатать байты в основном, был скопирован только первый символ файла. –

+0

@miguel: извините, я просто скопировал ваш код. Fread должен быть fread (байты, 1, размер, f). Прочтите man fread и код, который я написал, чтобы понять, почему. – rici

Смежные вопросы