2012-01-23 11 views
5

Sto scrivendo del codice che deve leggere fasta files, quindi parte del mio codice (incluso sotto) è un parser fasta. Poiché una singola sequenza può estendersi su più righe nel formato fasta, ho bisogno di concatenare più righe successive lette dal file in una singola stringa. Lo faccio, riallocando il buffer delle stringhe dopo aver letto ogni riga, per essere la lunghezza corrente della sequenza più la lunghezza della riga letta. Faccio qualche altra cosa, come lo spoglio dello spazio bianco ecc. Tutto va bene per il prima sequenza, ma i file fasta possono contenere più sequenze. Allo stesso modo, ho una matrice dinamica di strutture con due stringhe (titolo e sequenza effettiva), essendo "char *". Di nuovo, mentre incontro un nuovo titolo (introdotto da una riga che inizia con '>') incremento il numero di sequenze e rialloco il buffer dell'elenco di sequenze. Il realloc segfaults sull'allocazione dello spazio per la seconda sequenza conUtilizzo di realloc per espandere il buffer durante la lettura dal file si blocca

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

Per la vita di me non riesco a capire perché. Ho eseguito attraverso gdb e tutto sembra funzionare (cioè tutto viene inizializzata, i valori sembrano sani di mente) ... Ecco il codice:

#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

Grazie per tutti i commenti sulla routine di sottostringa. L'ho risolto nel mio codice. Ho notato anche che il modo in cui gestivo gli indici negativi era sbagliato. Dovrei aggiungere l'indice negativo, non sottrarlo. Detto questo, ho anche realizzato che ho copiato la funzione substr in errore, poiché non la chiamo nel resto del codice incollato. – sirlark

+0

'strip()' è anche bacato. Farà cose cattive con stringhe di lunghezza zero. Sembra che tu non lo chiami con tali stringhe, ma penso che sarebbe una buona cosa da sistemare quando è usato altrove. –

risposta

1

Penso che il problema di corruzione della memoria potrebbe essere il risultato di come si gestiscono i dati utilizzati nelle chiamate getline(). Fondamentalmente, lo line viene riallocato tramite strndup() nelle chiamate allo strip(), pertanto la dimensione del buffer che viene monitorata in linelen entro il getline() non sarà più accurata. getline() potrebbe sovraccaricare il buffer.

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

È possibile ottenere alcune funzioni semplici, testate per il ridimensionamento delle stringhe qui: http://stackoverflow.com/a/2452438/12711 L'uso di 'trim()' da quel collegamento risolverà questo problema (e l'altra latente bug (s) nella funzione 'strip()'). –

+0

Ho risolto tutti i problemi con strip (e substr) e ho ancora riscontrato il problema. L'interazione con getline e linelen fu sicuramente il problema. Grazie per tutto l'aiuto – sirlark

3

Un problema potenziale è qui:

strncpy(ret,s,j-i); 
return ret; 

ret potrebbe non ottenere un terminatore null. Vedi man strncpy:

 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. 

C'è anche un bug qui:

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

E se strlen(*s) è 0? Finirai a leggere (*s)[-1].

Non è inoltre possibile verificare in strip() che la stringa non consista interamente di spazi. Se lo fa, finirai con j < i.

modifica: Ho appena notato che la tua funzione substr() non viene effettivamente chiamata.

4

Si dovrebbe usare le stringhe che simile a questa:

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

Questo impedisce bug strncpy come quello che sembra che hai visto, e permette di fare strcat e gli amici più veloci.

Si dovrebbe anche utilizzare una matrice di raddoppio per ogni stringa. Ciò impedisce troppe assegnazioni e memcpys. Qualcosa di simile a questo:

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

ora vedo che si sta facendo la bioinformatica, il che significa che probabilmente bisogno di più prestazioni di quanto pensassi. Si dovrebbe usare le stringhe come questo, invece:

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

questo modo, quando si assegnano un oggetto stringa, si chiama malloc(sizeof(struct string) + len) ed evitare una seconda chiamata a malloc. È un po 'più di lavoro, ma dovrebbe aiutare in modo misurabile, in termini di velocità e anche di frammentazione della memoria.

Infine, se questo non è in realtà la fonte di errore, sembra che tu abbia qualche corruzione. Valgrind dovrebbe aiutarti a rilevarlo se gdb fallisce.

+0

@lief: il consumo di memoria è più di un problema che di velocità. Non voglio allocare in pezzi doppi e sprecare spazio. Certo, questo non è un problema nel parser fasta, più nell'elaborazione. – sirlark

+0

A causa della frammentazione interna del malloc, si può finire per usare troppa memoria anche se non la si richiede. Gli array di raddoppio sono abbastanza affidabili, ma se sono troppo spaventosi, per favore usa almeno qualcosa come "malloc_usable_size" per misurare la tua frammentazione. Se scegli di raddoppiare gli array e il mio secondo suggerimento di allocare insieme la lunghezza e il buffer delle stringhe, fai attenzione a includere la lunghezza nel calcolo della dimensione, oppure potresti finire con una terribile frammentazione (se assegni 2^n + sizeof int , per esempio). – leif

+0

'char ptr [0];' non è valido C. Si intende 'char ptr [];', ma questo è probabilmente un brutto nome per l'elemento ora poiché è un array, non un puntatore. Lo chiamerei 'data' o' contents' o qualcosa del genere. –

Problemi correlati