(ИМХО) Лучше всего прочитать весь файл в один большой буфер (также доступен вариант mmap, если он доступен), затем найти и исправить окончания строк и заголовки fasta. Это также уменьшит фрагментацию памяти. И это сильно упрощает «парсер».
РЕДАКТИРОВАТЬ: добавлен источник (он не идеален, но в прошлый раз, когда я проверял его, он работал ;-) Возможно, он неполон, я оторвал его от более крупной программы.
struct fastapart {
char * name;
char * data;
unsigned size;
struct roedel *friends;
};
struct fastafile {
size_t totsize;
char *tot;
unsigned count;
struct fastapart *parts;
int *alloc;
};
struct fastafile * read_complete_fasta(char *name)
{
int rc,state;
struct fastafile * result;
size_t pos,len,cnt,idx;
struct strbuff *fwd=NULL,*rev = NULL;
result = malloc (sizeof *result);
if (!result) return NULL;
result->tot = read_complete_file(name , &result->totsize);
if (!result->tot) goto failfree;
result->count = 0;
result->parts = NULL;
for (pos=cnt=state=0; pos < result->totsize; ) {
switch (state) {
case 0: /* find first '>' */
if (result->tot[pos] == '>') { pos++; state=2; continue; }
pos += strcspn( result->tot+pos, "\n" );
case 1: /* not found: sync to newline */
if (result->tot[pos] == '\n') { pos++; state=0; continue; }
else pos++;
continue;;
case 2: /* Got '>'; grab name */
len = strcspn( result->tot+pos, " \t\n" );
if (cnt >= result->count) {
size_t siz;
siz = result->count ? 2* result->count: 16;
result->parts = realloc( result->parts
, siz * sizeof *result->parts);
for ( ; result->count < siz;result->count ++) {
result->parts[cnt].name = NULL;
result->parts[cnt].data = NULL;
result->parts[cnt].friends = NULL;
result->parts[cnt].size = 0;
}
}
result->parts[cnt].name = result->tot+pos;
result->parts[cnt].name[len] = 0;
pos += 1+len;
len = strspn( result->tot+pos, " \t\n" );
pos += len;
state++;
continue;
case 3: /* grab data; for the moment, throw away reversed data */
if (result->tot[pos] == '>') {
if (fwd) {
memcpy(result->parts[cnt].data, fwd->data, fwd->used ); result->parts[cnt].size = fwd->used;
result->parts[cnt].data [ fwd->used ] = 0;
fwd->used = 0; }
if (rev) {
/* memcpy(result->parts[cnt].data+result->parts[cnt].size, rev->data, rev->used ); */
rev->used = 0;
}
if (result->parts[cnt].data) cnt++;
pos++; state=2;
continue;
}
len = strcspn( result->tot+pos, "\t\n" );
if (!len) { /* empty line; what to do? skip it! */
fprintf(stderr, "Empty\n" );
pos++; state=1;
continue; }
if (!result->parts[cnt].data) {result->parts[cnt].data = result->tot+pos; }
fwd = strbuff_add(fwd, result->tot+pos, len);
pos += len;
if (result->tot[pos] == '\t' ) {
pos += strspn(result->tot+pos, " \t" );
len = strcspn( result->tot+pos, "\n" );
rev = strbuff_add(rev, result->tot+pos, len);
pos += len;
}
pos += strspn(result->tot+pos, " \t\r\n" );
}}
if (state == 3) {
if (fwd) {
memcpy(result->parts[cnt].data, fwd->data, fwd->used ); result->parts[cnt].size = fwd->used;
result->parts[cnt].data [ fwd->used ] = 0;
fwd->used = 0;
}
if (rev) {
/* memcpy(result->parts[cnt].data+result->parts[cnt].size, rev->data, rev->used ); */
rev->used = 0;
}
if (result->parts[cnt].data) cnt++;
}
/* final realloc */
result->parts = realloc( result->parts, cnt * sizeof *result->parts);
result->count = cnt;
free (fwd);
free (rev);
result->alloc = malloc( result->count * sizeof result->alloc[0] );
if (result->alloc) {
for (cnt = 0; cnt < result->count; cnt++ ) result->alloc[cnt] = cnt;
}
return result;
failfree:
free (fwd);
free (rev);
free (result);
return NULL;
}
char * read_complete_file(char *name, size_t *sizep)
{
int fd, rc;
size_t size, len;
char *result;
struct stat st;
fd = open(name, O_RDONLY);
if (fd == -1) goto fail;
rc = fstat(fd, &st);
if (rc == -1) goto closefail;
result = malloc (1+st.st_size );
if (!result ) goto closefail;
result[st.st_size] = 0;
for (size = 0; size < st.st_size;) {
rc = read(fd, result, st.st_size - size);
if (rc < 0) goto freeclosefail;
size += rc;
}
fprintf(stderr, "Read %lu bytes FROM %s\n"
, (unsigned long) size, name);
close(fd);
*sizep = size;
return result;
freeclosefail:
free(result);
closefail:
close(fd);
fail:
*sizep=0; return NULL;
}