Как читать строку за строкой некоторый файл в C с помощью fscanf (), не получая фантомную строку? - PullRequest
0 голосов
/ 21 января 2020

В моем коде есть функция, которая читает строку файла построчно и создает из них структуры. Профессор сказал мне, что может быть проблема, и упомянул фантомную линию. Может кто-то увидеть мою функцию и объяснить мне, в чем проблема?

Это мой код:

void readComponentList(ComponentList *cl, char *fileName)
{
   FILE *file = fopen(fileName, "r");
   if (file == NULL) { perror(fileName);  exit(1); } // If the file doesn't exist, the program termitates with exit code 1
   int r;
   Component *c = newComponent(cl);
   // Creates useful component c by inserting line informations as arguments in the structure
   r = fscanf(file, "%24s %24s %24s %d %lf", c->type, c->model, c->unit, &(c->weight), &(c->price));
   while (r != EOF) // Doing the same thing for the rest of the lines
   {
      c = newComponent(cl);
      r = fscanf(file, "%24s %24s %24s %d %lf", c->type, c->model, c->unit, &(c->weight), &(c->price);
      // Since EOF only occurs after an unsuccessful read attempt, an additional "phantom line" with undefined content is read in here, which could lead to crashes.

   }
   fclose(file);
}

Это пример файла, который я читаю:

Motor M5x40 Stk 5 0.05
Elevator M5x60 Stk 6 0.05
ACM L-H-100 Stk 1250 530
SSM L-100 Stk 0 0
ElevatorW W3 Stk 0 0
Metal- kg 1000 344200

Компонент и структуры ComponentList:

typedef struct
{
   char     type[25];
   char     model[25];
   char     unit[25];
   int      weight;
   double   price;
   StepList *construction_steps;
} Component;

typedef struct
{
   Component **components;
   int count;
   int allocated;
} ComponentList;

1 Ответ

0 голосов
/ 21 января 2020

Это не ясно из опубликованного кода, но я предполагаю, что строка

Component *c = newComponent(cl);

будет 1) Создать новый элемент Компонента и 2) Вставить его в список cl

Так когда вы делаете это перед вызовом fscanf, элемент уже вставлен **, даже если fscanf не удается. Следовательно, вы получите «пустой» элемент в списке (он же фантомный элемент).

Чтобы избежать необходимости перемещать строку: Component *c = newComponent(cl);, чтобы она вызывалась только после успешный scanf.

Возможно, что-то вроде:

Component tmp;  // Scan into a local tmp variable
while (1)
{
   r = fscanf(file, "%24s %24s %24s %d %lf", 
                    tmp.type, tmp.model, tmp.unit, &tmp.weight, &tmp.price;

   if (r == EOF) break;

   if (r == 5)
   {
       // A complete struct was read so put it into the list
       c = newComponent(cl);
       *c = tmp;   // Copy the tmp struct into the new element in the list
   }
   else
   {
       // Didn't get the expected number of vars scanned.
       // Add error handling ....
   }
}

РЕДАКТИРОВАТЬ

Поскольку OP касается линии Component tmp;, это альтернативный подход:

char     type[25];
char     model[25];
char     unit[25];
int      weight;
double   price;

while (1)
{
   r = fscanf(file, "%24s %24s %24s %d %lf", 
                    type, model, unit, &weight, &price;

   if (r == EOF) break;

   if (r == 5)
   {
       // A complete struct was read so put it into the list
       c = newComponent(cl);
       strcpy(c->type, type);
       strcpy(c->model, model);
       strcpy(c->unit, unit);
       c->weight = weight;
       c->price = price;
   }
   else
   {
       // Didn't get the expected number of vars scanned.
       // Add error handling ....
   }
}
...