2012-01-23 20 views
5

私はコード(一部に含まれています)の一部がfastaパーサーであるように、fasta filesを読み出す必要のあるコードを書いています。単一のシーケンスはfasta形式の複数の行にまたがることができるので、ファイルから読み込んだ複数の連続する行を連結して単一の文字列にする必要があります。これは、各行を読み込んだ後に文字列バッファをrealloc'ingすることで、シーケンスの現在の長さと読み込まれた行の長さになるようにします。私は空白を取り除くなど、いくつかの作業を行います。ファーストファイルは複数のシーケンスを含むことができます。同様に、私は2つの文字列(タイトルと実際のシーケンス)を持つ構造体の動的配列を "char *"として持っています。繰り返しますが、新しいタイトル( '>'で始まる行で紹介されます)が出現すると、シーケンスの数を増やしてシーケンスリストバッファを再割り当てします。私は理由を見ることができない私の人生のためにファイルクラッシュから読み込み中にreallocを使ってバッファを展開する

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

と第二の配列のためのスペースを割り当てる上のreallocのセグメンテーションフォルト。

#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

サブストリングルーチンに関するすべてのコメントをありがとう。私は自分のコードで修正しました。しかし、負のインデックスを扱う方法が間違っていたことにも気付きました。私は、負のインデックスを追加する必要があります、それを引くことはありません。つまり、私は、貼り付けられたコードの残りの部分では呼び出さないので、substr関数を誤ってコピーしたことに気付きました。 – sirlark

+0

'strip()'もバグです。長さのゼロの文字列で悪いことをするでしょう。このような文字列では呼び出さないように見えますが、他の場所で使用されているときに修正するのは良いことだと思います。 –

答えて

1

メモリ破損の問題は、getline()コールで使用されているデータをどのように処理しているかの結果である可能性があります。基本的にはstrip()の呼び出しでlinestrndup()で再割り当てされるため、linelenで追跡されるバッファサイズはgetline()ではなくなります。 getline()がバッファをオーバーランさせる可能性があります。

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

ここでは、文字列をトリムするための素敵でシンプルでテストされた関数を得ることができます:http://stackoverflow.com/a/2452438/12711このリンクから 'trim()'を使うと、この問題が解決します。 'strip()'関数内のバグ)。 –

+0

strip(とsubstr)の問題をすべて修正しましたが、まだ問題がありました。 getlineとlinelenとのやりとりは、明らかに問題でした。すべての助けをありがとう – sirlark

3

一つの潜在的な問題はここにある:

strncpy(ret,s,j-i); 
return ret; 
私は、GDBを通してそれを実行したとすべてが(つまりすべてが、値がSANE思わ初期化される)動作しているようだ...ここでは、コードです

retはヌルターミネータを取得しないことがあります。 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. 

バグがここにもあります:

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

strlen(*s)が0ですか?あなたは(*s)[-1]と読むことになります。

また、strip()では、文字列が完全に空白ではないことを確認しません。そうであれば、j < iになります。

編集:ちょうどあなたのsubstr()機能が実際に呼び出されないことに気付きました。

4

あなたはこのような何かを見て文字列を使用する必要があります。

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

をあなたが見た思われるもののようにはstrncpyバグを防ぎ、あなたがより速くSTRCATや友人を行うことができます。

また、各文字列に2倍の配列を使用する必要があります。これは、あまりにも多くの割り当てとmemcpysを防ぎます。

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

私はあなたがおそらく私が思った以上のパフォーマンスを必要とすることを意味するバイオインフォマティクスを行っているのを見ています。あなたは、文字列オブジェクトを割り当てるとき、あなたはmalloc(sizeof(struct string) + len)を呼び出し、malloc関数への2回目の呼び出しを避けるため、この方法で

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

:あなたは、代わりにこのような文字列を使用する必要があります。これはもう少し作業ですが、スピードとメモリの断片化の点では、測定可能な助けとなるはずです。

最後に、これが実際にエラーの原因ではない場合は、何らかの破損があるように見えます。 Valgrindは、gdbが失敗した場合にそれを検出するのに役立ちます。

+0

@ lief:メモリ消費はスピードよりも重要です。私は塊を倍増させ、スペースを無駄にしたくない。確かに、これはfastaパーサーの問題ではなく、処理の詳細です。 – sirlark

+0

内部mallocフラグメンテーションのため、要求していなくてもあまりにも多くのメモリを使用することになります。配列を二倍にすることはとても信頼できますが、あまりにも恐ろしい場合は、少なくとも、malloc_usable_sizeのようなものを使用して断片化を測定してください。倍増配列を選択し、長さと文字列バッファを一緒に割り当てるという2番目の提案の場合、サイズ計算に長さを含めるように注意してください。または、恐ろしい断片化に終わることがあります(2^n + sizeof int 、 例えば)。 – leif

+0

'char ptr [0];'は無効です。C. 'char ptr [];'を意味しますが、ポインタではなく配列であるため、これはおそらく悪い名前です。私はそれを 'data'や' contents'などと呼んでいます。 –

関連する問題