2012-01-23 13 views
5

Estoy escribiendo un código que necesita leer fasta files, por lo que parte de mi código (incluido a continuación) es un analizador de Fasta. Como una sola secuencia puede abarcar varias líneas en formato fasta, necesito concatenar varias líneas sucesivas leídas del archivo en una sola cadena. Hago esto, reasignando el búfer de cadena después de leer cada línea, para ser la longitud actual de la secuencia más la longitud de la línea leída adentro. Hago algunas otras cosas, como quitar el espacio en blanco, etc. Todo va bien para el primera secuencia, pero los archivos fasta pueden contener múltiples secuencias. De manera similar, tengo una matriz dinámica de estructuras con dos cadenas (título y secuencia real), que es "char *". De nuevo, cuando encuentro un nuevo título (introducido por una línea que comienza con '>') incremento el número de secuencias y realococo el buffer de la lista de secuencias. Los segfaults REALLOC sobre la asignación de espacio para la segunda secuencia conUso de realloc para expandir el búfer al leer el archivo falla

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

Porque la vida de mí no puedo ver por qué. Me he encontrado a través del BGF y todo parece estar funcionando (es decir, todo lo que es inicializado, los valores parece cuerdo) ... Aquí está el código:

#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

Gracias por todos los comentarios sobre la rutina subserie. Lo he arreglado en mi código. También noté que la forma en que manejaba los índices negativos era incorrecta. Debería agregar el índice negativo, no restarlo. Dicho esto, también me di cuenta de que copié la función substr por error, ya que no la llamo en el resto del código pegado. – sirlark

+0

'strip()' también tiene errores. Hará cosas malas con cadenas de longitud cero. Parece que no lo llamas así, pero creo que sería bueno solucionarlo cuando se use en otro lugar. –

Respuesta

1

Creo que el problema de corrupción de memoria podría ser el resultado de cómo maneja los datos utilizados en sus llamadas getline(). Básicamente, line se reasigna a través de strndup() en las llamadas al strip(), por lo que el tamaño del búfer que se rastrea en linelen por getline() ya no será preciso. getline() puede rebasar el 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

Puede obtener algunas funciones bonitas, simples y probadas para ajustar cadenas en el lugar aquí: http://stackoverflow.com/a/2452438/12711 Usando 'trim()' de ese enlace resolverá este problema (y el otro latente) error (s) en la función 'strip()'). –

+0

Solucioné todo el problema con strip (y substr) y todavía tenía el problema. La interacción con getline y linelen fue definitivamente el problema. Gracias por toda la ayuda – sirlark

3

Un problema potencial es aquí:

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

ret puede no obtener un terminador nulo. Ver 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. 

También hay un error aquí:

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

¿Qué pasa si strlen(*s) es 0? Terminará leyendo (*s)[-1].

Tampoco verifique en strip() que la cadena no consiste únicamente en espacios. Si lo hace, terminará con j < i.

editar: Acaba de notar que su función substr() en realidad no se llama.

4

que puedes usar cadenas que parecen algo como esto:

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

Esto evita errores strncpy como lo que parece que viste, y le permite hacer amigos más rápidos y strcat.

También debe usar una matriz de duplicación para cada cadena. Esto evita demasiadas asignaciones y memcpys. Algo como esto:

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

ahora veo que está haciendo la bioinformática, lo que significa que es probable que tenga un mayor rendimiento de lo que pensaba. Debe utilizar cuerdas esta forma:

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

De esta manera, cuando se asigna un objeto de cadena, se llama a malloc(sizeof(struct string) + len) y evitar una segunda llamada a malloc. Es un poco más de trabajo, pero debería ser medible, en términos de velocidad y también de fragmentación de la memoria.

Por último, si esta no es realmente la fuente de error, parece que tiene algún daño. Valgrind debería ayudarlo a detectarlo si gdb falla.

+0

@lief: el consumo de memoria es más un problema que la velocidad. No quiero asignar doblando trozos y perder el espacio. Es cierto que esto no es un problema en el analizador sintáctico Fasta, más en el procesamiento. – sirlark

+0

Debido a la fragmentación interna de malloc, puede terminar usando demasiada memoria incluso si no la solicita. Las matrices de duplicación son bastante confiables, pero si son demasiado aterradoras, al menos use algo como 'malloc_usable_size' para medir su fragmentación. Si elige doblar matrices, y mi segunda sugerencia de asignar la longitud y el búfer de cadenas juntas, tenga cuidado de incluir la longitud en el cálculo de tamaño, o puede terminar con una fragmentación horrible (si asigna 2^n + sizeof int , por ejemplo). – leif

+0

'char ptr [0];' no es válido C. Quiere decir 'char ptr [];', pero probablemente este sea un mal nombre para el elemento ahora, ya que es una matriz, no un puntero. Yo lo llamaría 'datos' o' contenidos' o algo por el estilo. –

Cuestiones relacionadas