Чтение из двоичного файла с записями переменной длины - PullRequest
2 голосов
/ 07 мая 2009

У меня есть двоичный файл с записью переменной длины, который выглядит примерно так:

12  economic10
13  science5
14  music1 
15  physics9
16  chemistry9
17  history2 
18  anatomy7 
19  physiology7
20  literature3
21  fiction3
16  chemistry7
14  music10 
20  literature1

Имя курса - единственная запись переменной длины в файле, первое число - это код курса, и это может быть число от 1 до 9999, а 2-е число - это отдел, и оно может быть между 1 и 10. Как видно из файла, между названием курса и номером кафедры нет пробела.

Вопрос в том, как я могу прочитать из двоичного файла? В этом файле нет поля, в котором можно указать размер строки, которая является названием курса. Я могу читать первый int (id курса) нормально, но как мне узнать, каков размер названия курса?

Ответы [ 6 ]

3 голосов
/ 07 мая 2009

Используйте fscanf() со строкой формата "%u %[a-z]%u".

Вот полный пример программы:

#include <stdio.h>

#define NAME_MAX 64

int main(int argc, char ** argv)
{
    FILE * file = fopen("foo.txt", "rb");
    unsigned int course, department;
    char name[NAME_MAX];

    while(fscanf(file, "%u %[a-z]%u", &course, name, &department) != EOF)
    {
        // do stuff with records
        printf("%u-%u %s\n", department, course, name);
    }

    fclose(file);

    return 0;
}
1 голос
/ 07 мая 2009

Как уже говорили другие, это очень похоже на текст, поэтому подход с парсингом текста, вероятно, будет правильным решением. Поскольку это домашняя работа, я не собираюсь ее кодировать, но вот общий подход, который я бы выбрал:

  • Используя fscanf, прочитайте код курса и объединенную строку с именем и кодом отдела.
  • Начиная с конца объединенной строки, возвращайтесь назад, пока не найдете первую не цифру. Это конец названия курса.
  • Считать целое число, начинающееся сразу после конца имени курса (т. Е. Последнюю цифру, которую мы находим при сканировании в обратном направлении).
  • Заменить первый символ этой части целого числа строки на NUL ('\ 0') - это завершает объединенную строку сразу после имени курса. Таким образом, все, что мы оставили в объединенной строке, это название курса, и у нас есть код курса и код отдела в целочисленных переменных.
  • Повторите для следующей строки.
0 голосов
/ 07 мая 2009

Предполагается, что нет возврата каретки, и каждая строка завершается нулем. Я написал небольшую программу для создания бинарного файла, а затем прочитал его обратно, выдав аналогичный результат.


// binaryFile.cpp
#include "stdafx.h"
#include <stdio.h>
#include <string.h>

#define BUFSIZE 64
int _tmain(int argc, _TCHAR* argv[])
{
  FILE *f;
  char  buf[BUFSIZE+1];

  // create dummy bin file
  f = fopen("temp.bin","wb");
  if (f)
  { // not writing all the data, just a few examples
   sprintf(buf,"%04d%s\00",12,"economic10"); fwrite(buf,sizeof(char),strlen(buf)+1,f);
   sprintf(buf,"%04d%s\00",13,"science5"); fwrite(buf,sizeof(char),strlen(buf)+1,f);
   sprintf(buf,"%04d%s\00",14,"music1"); fwrite(buf,sizeof(char),strlen(buf)+1,f);
   sprintf(buf,"%04d%s\00",15,"physics9"); fwrite(buf,sizeof(char),strlen(buf)+1,f);
   fclose(f);
  }
  // read dummy bin file
  f = fopen("temp.bin","rb");
  if (f)
  {
   int classID;
   char str[64];
   char *pData
   long offset = 0;
   do
   {
    fseek(f,offset,SEEK_SET);
    pData = fgets(buf,BUFSIZE,f);
    if (pData)
    {  sscanf(buf,"%04d%s",&classID,&str);
       printf("%d\t%s\r\n",classID,str);
       offset +=strlen(pData)+1;    // record + 1 null character
     }
    } while(pData);
    fclose(f);
   }
   getchar();
   return 0;
}

0 голосов
/ 07 мая 2009

Если между кодом курса и названием курса (включая код отдела) есть однозначное соответствие, вы можете определить размер названия курса из его кода с помощью заранее определенной таблицы где-то в коде или в файле конфигурации.

Если нет, то основная проблема, которую я вижу, заключается в различении таких вещей, как music1 и music10.

0 голосов
/ 07 мая 2009

Вам нужно знать, как файл был записан в первую очередь.

0 голосов
/ 07 мая 2009

Чтобы читать записи переменной длины, вы должны использовать какое-то соглашение. Например, специальные символы, обозначающие конец записи. Внутри каждой записи вы можете использовать другой специальный символ, обозначающий конец поля.

DO_READ    read from file
           is END_OF_RECORD char present?
           yes: GOTO DO_PROCESS
           no : GOTO DO_READ

DO_PROCESS read into buffer
           is END_OF_FILE mark present?
           yes: GOTO DOSOMETHINGWITHIT
           no:  GOTO DO_PROCESS
...