#c #file #line #back
#c #файл #строка #Назад
Вопрос:
Я должен прочитать текстовый файл, который может начинаться с необязательных комментариев. На практике мне приходится пропускать любую строку в начале файла, которая не начинается с ‘@’ или ‘>’. В моем тестовом примере файл выглядит так:
# Sun Jul 12 22:04:52 2009 /share/apps/corona/bin/filter_fasta.pl --output=/data/results/solid0065/primary.20090712170542775
# Cwd: /state/partition1/home/pipeline
# Title: solid0065_20090629_FC1_Tomate_Heinz_4_5_Kb_Tomate_Heinz_4_5_Kb_01
>125_963_316_F3
T1230330231223011323010013
Поэтому я должен пропустить первые 3 строки (но в целом я должен пропустить n строк). Я должен повторить это с 2 или 4 файлами [которые находятся внутри FILE ** inputFiles]. Я пробовал с этим циклом:
buffer = (char*) malloc (sizeof(char) * 5000);
if (buffer == NULL)
notEnoughMemory();
for (i = 0; i < (cIn-1); i ){
fgetpos(inputFiles[i], amp;position);
fgets(buffer, 4999, inputFiles[i]);
while ((buffer[0] != '@') amp;amp; (buffer[0] != '>')){
fgetpos(inputFiles[i], amp;position);
fgets(buffer, 4999, inputFiles[i]);
}
fsetpos(inputFiles[i], amp;position);
}
Где cIn — это number_of_input_files 1 .
При попытке ее отладки цикл корректно останавливается после чтения четвертой строки. Но когда я использую setpos, он возвращается не к началу четвертой строки, как я ожидал, а к середине третьей.
На самом деле, если точно после fsetpos() я печатаю буфер после этих операций:
fgets(buffer, 4999, inputFiles[i]);
fgets(buffer, 4999, inputFiles[i]);
Я получаю:
FC1_Tomate_Heinz_4_5_Kb_Tomate_Heinz_4_5_Kb_01
>125_963_316_F3
Есть идеи?
Заранее спасибо
Ответ №1:
Вместо fgetpos(); fsetpos();
того, чтобы вы могли бы использовать
fseek(inputFiles[i], -strlen(buffer), SEEK_CUR);
Комментарии:
1.Это не работает. Если я попытаюсь использовать fseek(входные файлы [i], -strlen(buffer), SEEK_CUR); fgets(buffer, 4999, входные файлы [i]); То я получу: C1_Tomate_Heinz_4_5_Kb_Tomate_Heinz_4_5_Kb_01
2. я только что попробовал, и это сработало ‘fgets(buffer, 4999, file); while ((buffer[0] != ‘@’) amp;amp; ( буфер[0] != ‘>’)){ fgets(буфер, 4999, файл); } fseek(файл, -strlen(буфер), SEEK_CUR); fgets(буфер, 4999, файл); printf(«%s», буфер);’
3. Единственная проблема, которую я могу себе представить, заключается в том, что fgets не завершается null должным образом, но это было бы действительно странно.
4. strlen(buffer) равен 16, что правильно (строка «>» заканчивается символом «n»), так что проблема не в этом. Я считаю, что что-то не так с текущей позицией в файле … Mah…
5. Этот ответ неверен для файлов, открытых в текстовом режиме. Согласно 7.21.9.4 функции ftell , параграф 2 стандарта C11 : «Для текстового потока его индикатор положения файла содержит неопределенную информацию, используемую функцией fseek для возврата индикатора положения файла для потока в его положение во время вызова ftell; разница между двумя такими возвращаемыми значениями равнане обязательно является значимой мерой количества записанных или прочитанных символов «.
Ответ №2:
(ИМХО) Лучше всего прочитать весь файл в один большой буфер (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 , amp;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, " tn" );
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, " tn" );
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, "tn" );
if (!len) { /* empty line; what to do? skip it! */
fprintf(stderr, "Emptyn" );
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, " trn" );
}}
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 resu<
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 *resu<
struct stat st;
fd = open(name, O_RDONLY);
if (fd == -1) goto fail;
rc = fstat(fd, amp;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 %sn"
, (unsigned long) size, name);
close(fd);
*sizep = size;
return resu<
freeclosefail:
free(result);
closefail:
close(fd);
fail:
*sizep=0; return NULL;
}
Комментарии:
1. Размер файла может достигать 60 ГБ. И их от 2 до 4. Не удалось загрузить весь файл в память, даже если бы я хотел
2. Если вы сопоставляете геном человека, вы, вероятно, можете позволить себе 64-битную машину и mmap весь файл.
3. Это проект колледжа, я на самом деле не отображаю геном человека (я даже не знаю, человек ли это …). Я мог бы наверняка создать тестовый файл меньшего размера, который поместится на моем ноутбуке объемом 4 ГБ, но на данный момент меня больше интересует пропуск этих 3 строк. Я не думаю, что при работе с миллиардами байт данных пропуск 3 строк таким образом создает узкое место в производительности.. Мне так проще. Я не могу поверить, что на C нет простого способа сделать это
4. Это не невозможно сделать на домашнем ПК. 64 бита не такая уж редкость. Но: вам нужно запомнить только последние несколько строк, одну или две. для> строк: просто разделите их на 4 базы на байт и запишите их в двоичный файл. Вам не понадобится больше нескольких КБ буферного пространства. Можно было бы сделать на C64, учитывая достаточную емкость диска 😉
5. Я должен работать как с файлами FastQ, так и с файлами FastA. Я уже успешно реализовал часть FastQ (и она работает, протестирована), теперь я почти закончил и с FastA… Я не понимаю, почему вы говорите, что не можете обойтись домашним ПК. Как я уже сказал, я не секвенирую геному, мне нужно только устранить некоторые дубликаты 🙂
Ответ №3:
Вы можете просто пропустить обработку строк, которые вас не интересуют:
for (i = 0; i < (cIn-1); i ){
while (fgets(buffer, 4999, inputFiles[i])){
if(buffer[0] == '@' || buffer[0] == '>') {
puts(buffer);
}
/* else do nothing*/
}
}
Затем вы просто заменяете puts(buffer);
код, необходимый для обработки допустимых строк.
(хотя, из вашего примера похоже, что вы скорее хотите игнорировать только строки, начинающиеся с #
, ?)
Комментарии:
1. Это изменяет входной файл, не так ли? К сожалению, я не могу этого сделать.
2. @Alex Он просто читает файл до конца, файл на диске не изменяется.
3. Я прочитал fputs извините. Тогда я не уверен, как читать то, что я помещаю () … с помощью scanf()?
4. @Alex puts() был просто примером, и вы можете убедиться, что вы получаете правильные строки текста. Предполагается, что вы должны удалить этот вызов puts() и заменить его фактическим кодом, который вы хотите запустить для каждой из допустимых входных строк, которые вы читаете. Строка находится в
buffer
, поэтому, например, вызовите myfunction_that_does_something(buffer);. если это не то, что вам нужно, пожалуйста, объясните, чего вы пытаетесь достичь5. Поскольку я не понимал, почему fseek и fsetpos не работали, когда они должны были (скопировать и вставить некоторый рабочий исходный код и все еще получать ошибки!) Я решил немного изменить ситуацию и сделать что-то похожее на ваше предложение. Все еще интересно, почему fseek не работает.
Ответ №4:
Вы можете получить позицию в любой заданной точке. Это действительно полезно, когда вы проверяете значение null в состоянии while, но после входа внутрь вы хотите вернуть курсор на предыдущую строку.
fpos_t позиция;
fgetpos (file, amp;position);
Затем можно установить обратно в ту же позицию:
fsetpos (file, amp;position);
Пожалуйста, следуйте документам, это проверено и протестировано, работает нормально.
http://www.cplusplus.com/reference/cstdio/fgetpos /