2012-01-23 27 views
5

fasta files okunması gereken bir kod yazıyor, bu nedenle kodumun bir kısmı (aşağıda verilmiştir) bir fasta ayrıştırıcısıdır. Tek bir sıra, fasta biçiminde birden çok satıra yayılabildiğinden, dosyadan okunan birden çok satırı tek bir dizeye birleştirmem gerekir. Bunu, her satırı okuduktan sonra string arabelleğini okuyarak, dizinin mevcut uzunluğu ve okunan satırın uzunluğu olarak yeniden yazarak yapıyorum. Başka bir şey yapıyorum. İlk sıra, ancak fasta dosyaları çoklu dizileri içerebilir. Benzer şekilde, iki karakter dizisi (başlık ve gerçek sıra) ile "char *" olan dinamik bir yapı dizim var. Yine, yeni bir başlıkla karşılaştığımda ('>' ile başlayan bir satırla tanıştım), dizilerin sayısını artırıyorum ve sıra listesi arabelleğini yeniden yazarım. Ben neden göremiyorum Beni hayat içinDosyadan okunurken arabelleği genişletmek için realloc kullanma

*** glibc detected *** ./stackoverflow: malloc(): memory corruption: 0x09fd9210 *** 
Aborted 

ile ikinci sırası için alanı ayrılıyor üzerinde realloc çalışma sırasında parçalama arızası.

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <ctype.h> 
#include <math.h> 
#include <errno.h> 

//a struture to keep a record of sequences read in from file, and their titles 
typedef struct { 
    char *title; 
    char *sequence; 
} sequence_rec; 

//string convenience functions 

//checks whether a string consists entirely of white space 
int empty(const char *s) { 
    int i; 
    i = 0; 
    while (s[i] != 0) { 
     if (!isspace(s[i])) return 0; 
     i++; 
    } 
    return 1; 
} 

//substr allocates and returns a new string which is a substring of s from i to 
//j exclusive, where i < j; If i or j are negative they refer to distance from 
//the end of the s 
char *substr(const char *s, int i, int j) { 
    char *ret; 
    if (i < 0) i = strlen(s)-i; 
    if (j < 0) j = strlen(s)-j; 
    ret = malloc(j-i+1); 
    strncpy(ret,s,j-i); 
    return ret; 
} 

//strips white space from either end of the string 
void strip(char **s) { 
    int i, j, len; 
    char *tmp = *s; 
    len = strlen(*s); 
    i = 0; 
    while ((isspace(*(*s+i)))&&(i < len)) { 
     i++; 
    } 
    j = strlen(*s)-1; 
    while ((isspace(*(*s+j)))&&(j > 0)) { 
     j--; 
    } 
    *s = strndup(*s+i, j-i); 
    free(tmp); 
} 


int main(int argc, char**argv) { 
    sequence_rec *sequences = NULL; 
    FILE *f = NULL; 
    char *line = NULL; 
    size_t linelen; 
    int rcount; 
    int numsequences = 0; 

    f = fopen(argv[1], "r"); 
    if (f == NULL) { 
     fprintf(stderr, "Error opening %s: %s\n", argv[1], strerror(errno)); 
     return EXIT_FAILURE; 
    } 
    rcount = getline(&line, &linelen, f); 
    while (rcount != -1) { 
     while (empty(line)) rcount = getline(&line, &linelen, f); 
     if (line[0] != '>') { 
      fprintf(stderr,"Sequence input not in valid fasta format\n"); 
      return EXIT_FAILURE; 
     } 

     numsequences++; 
     sequences = realloc(sequences,sizeof(sequence_rec)*numsequences); 
     sequences[numsequences-1].title = strdup(line+1); strip(&sequences[numsequences-1].title); 
     rcount = getline(&line, &linelen, f); 
     sequences[numsequences-1].sequence = malloc(1); sequences[numsequences-1].sequence[0] = 0; 
     while ((!empty(line))&&(line[0] != '>')) { 
      strip(&line); 
      sequences[numsequences-1].sequence = realloc(sequences[numsequences-1].sequence, strlen(sequences[numsequences-1].sequence)+strlen(line)+1); 
      strcat(sequences[numsequences-1].sequence,line); 
      rcount = getline(&line, &linelen, f); 
     } 
    } 
    return EXIT_SUCCESS; 
} 
+0

Alt dizinin rutini hakkındaki tüm yorumlar için teşekkürler. Onu koduma yerleştirdim. Bununla birlikte, negatif indekslerle uğraşmamın yanlış olduğunu da fark ettim. Negatif indeksi eklemem, çıkarmamalıyım. Söylediğim gibi, altstr işlevini kopyaladım, yapıştırılan kodun geri kalanında aramadım. – sirlark

+0

'strip()' de buggy'dir. Sıfır uzunluklu dizelerle kötü şeyler yapar. Böyle dizelerle konuşmuyor gibi görünüyor, ama başka bir yerde kullanıldığında düzeltmek için iyi bir şey olacağını düşünüyorum. –

cevap

1

Bellek bozukluğu sorunu, getline() çağrılarınızda kullanılan verileri nasıl kullandığınızın bir sonucu olabileceğini düşünüyorum. Temel olarak, numaralı çağrılarda strndup() aracılığıyla line yeniden ayrılır, böylece linelengetline() tarafından izlenen arabellek boyutu artık doğru olmayacaktır. getline() arabelleği taşabilir.

while ((!empty(line))&&(line[0] != '>')) { 

    strip(&line); // <-- assigns a `strndup()` allocation to `line` 

    sequences[numsequences-1].sequence = realloc(sequences[numsequences-1].sequence, strlen(sequences[numsequences-1].sequence)+strlen(line)+1); 
    strcat(sequences[numsequences-1].sequence,line); 

    rcount = getline(&line, &linelen, f); // <-- the buffer `line` points to might be 
              //  smaller than `linelen` bytes 

} 
+0

Dizeleri burada yerinde kesmek için bazı güzel, basit, test edilmiş işlevler alabilirsiniz: http://stackoverflow.com/a/2452438/12711 Bu bağlantıdan 'trim() 'özelliği bu sorunu çözecektir (ve diğer gizli strip() işlevindeki hata (lar). –

+0

Tüm sorunu şerit (ve substr) ile düzelttim ve hala sorun yaşadım. Getline ve linelen ile etkileşim problemsizdi. Tüm yardım için teşekkürler – sirlark

3

potansiyel bir mesele buradadır:

strncpy(ret,s,j-i); 
return ret; 
Ben gdb ile çalıştırmak ve her şey (yani her şey, değerler aklı başında görünüyor başlatıldığını) çalışıyor gibi görünüyor ... İşte kod

ret, boş bir terminatör alamayabilir. man strncpy Bkz:

 char *strncpy(char *dest, const char *src, size_t n); 

     ... 

     The strncpy() function is similar, except that at most n bytes of src 
     are copied. Warning: If there is no null byte among the first n bytes 
     of src, the string placed in dest will not be null terminated. 

bir hata burada da var:

j = strlen(*s)-1; 
while ((isspace(*(*s+j)))&&(j > 0)) { 

ne olur strlen(*s) 0'dır? Sonunda (*s)[-1] değerini okuyacaksınız.

Ayrıca, strip() dizgesini de dizinin tamamen boşluk içermediğini kontrol etmiyorsunuz. Eğer yaparsa, j < i ile biter.

düzenleme: Sadece substr() işlevinin çağrılmadığını fark ettiniz.

4

Sen şuna benzer dizeleri kullanmalısınız: gördüğün göründüğü gibi gibi strncpy böcek önler

struct string { 
    int len; 
    char *ptr; 
}; 

ve strcat ve daha hızlı arkadaşlar yapmanızı sağlar.

Ayrıca, her dize için bir katlama dizisi kullanmalısınız. Bu çok fazla tahsis ve memcpys engeller. Böyle bir şey:

int sstrcat(struct string *a, struct string *b) 
{ 
    int len = a->len + b->len; 
    int alen = a->len; 
    if (a->len < len) { 
     while (a->len < len) { 
      a->len *= 2; 
     } 
     a->ptr = realloc(a->ptr, a->len); 
     if (a->ptr == NULL) { 
      return ENOMEM; 
     } 
    } 
    memcpy(&a->ptr[alen], b->ptr, b->len); 
    return 0; 
} 

Ben şimdi biyoinformatik yaptığını görmek muhtemelen düşündüğümden daha performans gerektiği anlamına gelir. Sen artık şöyle dizeleri kullanmalıdır: Bir dize nesne tahsis zaman

struct string { 
    int len; 
    char ptr[0]; 
}; 

Bu yolla, malloc(sizeof(struct string) + len) arayıp malloc ikinci bir çağrı kaçının. Biraz daha fazla çalışma ama hız ve hafıza parçalanması açısından ölçülebilir bir şekilde yardımcı olmalı.

Son olarak, bu aslında bir hata kaynağı değilse, bazı yolsuzluklarınız var gibi görünüyor. Eğer gdb başarısız olursa Valgrind onu tespit etmene yardımcı olmalı.

+0

@lief: Bellek tüketimi hızdan ziyade bir konudur. İki katına bölmek ve boşa harcanmak istemiyorum. Kuşkusuz bu, fasta ayrıştırıcısında, işlemede daha fazla bir sorun değildir. – sirlark

+0

Dahili malloc parçalanması nedeniyle, siz istemeseniz bile çok fazla bellek kullanabilirsiniz. İki katına çıkan diziler oldukça iyi bir şekilde güvendeler, ancak çok korkutucularsa, en azından parçalanmanızı ölçmek için 'malloc_usable_size' gibi bir şey kullanın. İkileme dizilerini ve uzunluk ve dize arabelleğini birlikte ayırma konusundaki ikinci öneriyi seçerseniz, uzunluk hesaplamasında uzunluğa yer verdiğinize dikkat edin veya korkunç parçalanma ile sonuçlanabilirsiniz (eğer 2^n +. , Örneğin). – leif

+0

'char ptr [0];' geçersiz C'dir. Char ptr []; 'dır, ama bu bir eleman değil, bir işaretçi olduğu için muhtemelen öğe için kötü bir addır. Buna “data” veya “content” veya bu satırlardaki bir şey diyebilirim. –