C — Попытка вернуться к предыдущей строке в файле

#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 /