Мой профессор успешно выполнил этот код, но я не могу.Кажется, проблема с fopen () и указателями - PullRequest
1 голос
/ 29 апреля 2019

Редактировать: если это имеет значение, я использую Dev C ++ с опцией -std = c99.

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

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

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

Вот метод с проблемой (я предполагаю)

int ReadFileStoreInList(void)
{ 
   FILE *cfPtr; 

   if ((cfPtr = fopen("Hertz-Homework-9-List.txt", "r")) !=NULL) 
   { 
      char  make[TWELVE]={""}, model[TWELVE]={""}, size[TWELVE]={""}, color[TWELVE]={""}, power[TWELVE]={""};
      char rented = 'A';
      float daily_rate=0.0;
      char dAilyRate[TWELVE]; 
      fscanf(cfPtr, "%s%s%s%s%s%s", make, model, size, color, power, dAilyRate);
      while (!feof(cfPtr)) 
      { 
         fscanf(cfPtr, "%s%s%s%s%s%f", make, model, size, color, power, &daily_rate);
         rented = 'A';  
         add_at_end(make, model, size, color, power, daily_rate, rented);
      } 
      printScreenTitleAndHeaderForCars();   
      traverse_in_order();
   }
   else 
   {
      puts("Input data file could not be opened, I have no new inventory of cars from headquarters\n\n\n");
   }
   fclose(cfPtr); 
}

Это вызывает traverse_in_order (); и printScreenTitleAndHeaderForCars () ;, который я перечислю ниже.

void traverse_in_order() 
{    
    node *ptr;  

    if(start==NULL) 
    {   
        printf("list is empty\n");        
        return;    
    }
    printScreenTitleAndHeaderForCars(); 

    for(ptr=start; ptr!=NULL; ptr=(*ptr).next)
         printf("%-12s%-12s%-12s%-12s%-12s%9.2f%12c\n", ptr->make, ptr->model, ptr->size, ptr->color, ptr->power, ptr->daily_rate, ptr->rented);
}  

void printScreenTitleAndHeaderForCars()
{
    system("Cls");
      printf("%35s\n\n","Hertz Rental Cars");
      printf("%79s\n","Avail");
      printf("%-12s%-12s%-12s%-12s%-12s%-12s%8s\n", "Make", "Model", "Size", "Color", "Power", "Daily_Rate", "Rented");

      for (int x=0; x< 7; x++)
         printf("----------- ");
      printf("\n");
}

В случае, если также необходим файл структуры / заголовка:

#define TWELVE 12

typedef struct node_type 
{   
    char make[TWELVE];
    char model[TWELVE];
    char size[TWELVE];
    char color[TWELVE];
    char power[TWELVE];
    float daily_rate;
    char rented;         
    struct node_type *next;
} node;

node *start=NULL;

int ReadFileStoreInList(void);

void add_at_beginning();
void add_at_end(char make[TWELVE], char model[TWELVE], char size[TWELVE], char color[TWELVE], char power[TWELVE], float daily_rate, char rented);
void add_after_element();
void add_before_element();
void traverse_in_order();
void traverse_in_reverse_order(node *);
void delete_at_beginning();
void delete_at_end();
void delete_after_element();
void delete_before_element();
void sort();
void doSomething();      // menu to ask operator what to do 
void RentaCar();
void FindCarAndUpdateAsRented(char *modelSelected);
void ReturnCar();
void toTitleCase(char *aString);
void printScreenTitleAndHeaderForCars();

Запрошенная функция add_at_end:

void add_at_end(char make[TWELVE], char model[TWELVE], char size[TWELVE], char color[TWELVE], char power[TWELVE], float daily_rate, char rented)
{    
    node *ptr, *loc;          
    ptr = (node *) malloc(sizeof(node));  
    if(ptr==NULL)            
    {  
    printf("no space\n");  
    return;    
    }

    strcpy((*ptr).make,make);    
    strcpy((*ptr).model,model);
    strcpy((*ptr).size,size);    
    strcpy((*ptr).color,color);    
    strcpy((*ptr).power,power);    
    (*ptr).daily_rate = daily_rate;
    (*ptr).rented = rented;
    if(start==NULL)            
    {  
        start=ptr;             
        (*start).next=NULL;    
    }
    else  
    {
        loc = start;                
        while((*loc).next != NULL)  
           loc=(*loc).next;
        (*loc).next=ptr;            
        (*ptr).next=NULL;          
    }  
}  

Достаточно интересно, что если я переименую этот файл во что-то неправильное, соответствующий текст printScreenTitleAndHeaderForCars () будет запущен и отобразится на экране. Вот почему я считаю, что это как-то связано с ReadFileStoreInList ();

Я пытался отлаживать это в течение нескольких часов, но со знанием, которое я изучил в классе, я просто не могу понять, почему это не работает.

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

Когда файл неправильно назван в моем коде, он запускает это:

                  Hertz Rental Cars

                                                                          Avail
Make        Model       Size        Color       Power       Daily_Rate    Rented
----------- ----------- ----------- ----------- ----------- ----------- -----------
list is empty

enter choice
 1. Select Model and Rent
 2. Select Model and Return
 5. traverse in order
 11. sort
 12. exit

Там, где находится "список пуст", он должен заполнить данные из текстового файла и поместить их туда.

Вместо этого я просто получаю:


--------------------------------
Process exited after 1.433 seconds with return value 3221225477
Press any key to continue . . .

У меня есть ощущение, что это связано с тем, как пишутся указатели, но я изо всех сил пытаюсь понять, как он мог выполнить код, а я не смог.

Будем признательны за любые знания о том, почему это может произойти!

Редактировать: Содержание текстового файла:

make, model, size, color, power,daily rate.
Mazda,3,4-door,Black,4-Cyl,$99.73 
Jeep,Cherokee,4-door,Blue,8-Cyl,$131.92 
Buick,Regal,4-door,Purple,6-Cyl,$125.19 
Fullsize,SUV,5-door,Brown,8-Cyl,$163.94 
Chrysler,Pacifica,4-door,Green,6-Cyl,$127.49 
Ford,Focus,2-door,Red,4-Cyl,$99.73 
VW,Jetta,2-door,Orange,4-Cyl,$94.91 
Chevrolet,Suburban,4-door,Yellow,8-Cyl,$204.92 
Nissan,Pathfinder,4-door,White,6-Cyl,$145.11 
Chevrolet,Spark,2-door,Teal,4-Cyl,$99.55 

Ответы [ 2 ]

2 голосов
/ 29 апреля 2019

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

Во-первых, вызов для чтения заголовка опасен:

      fscanf(cfPtr, "%s%s%s%s%s%s", make, model, size, color, power, dAilyRate);

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

      fscanf(cfPtr, "%*[^\n]");

* в формате говорит, что директива поля не должна быть назначена, только для чтения. В целом, это читает (и игнорирует) все до, но не включая первый перевод строки. Это также позволяет вам избавиться от имени dAilyRate с отвратительным именем.

Тогда есть формат для чтения фактических данных:

         fscanf(cfPtr, "%s%s%s%s%s%f", make, model, size, color, power, &daily_rate);

Это просто не соответствует данным. В частности, дескриптор поля %s пропускает начальный пробел и соответствует строке с пробелами *1025*. Ваши данные разделены запятыми, а не пробелами, кроме разделителей строк. В результате этот вызов scanf попытается записать данные из всей строки в каждую из первых пяти строк, тем самым обойдя каждую из их границ. Это правдоподобная причина сегфо.

Более того, строка, считанная в daily_rate, не будет выполнена, поскольку следующие данные, доступные в этой точке, будут нечисловыми. Тем не менее, даже если запятые были заменены на пробелы, данные о скорости все равно не будут считываться правильно, поскольку $ не является допустимой частью числа. А это, в свою очередь, скинет чтение для второй и последующих строк.

Переполнения полей можно было бы избежать, указав максимальную ширину поля в формате. Более того, было бы целесообразно проверить возвращаемое значение scanf(), чтобы убедиться, что все поля были прочитаны, как продемонстрировал @Achal в своем ответе, прежде чем полагаться на эти данные.

Вот файл данных в формате, совместимом с форматами, которые вы фактически используете:

make model size color power rate
Mazda 3 4-door Black 4-Cyl 99.73 
Jeep Cherokee 4-door Blue 8-Cyl 131.92 
Buick Regal 4-door Purple 6-Cyl 125.19 
Fullsize SUV 5-door Brown 8-Cyl 163.94 
Chrysler Pacifica 4-door Green 6-Cyl 127.49 
Ford Focus 2-door Red 4-Cyl 99.73 
VW Jetta 2-door Orange 4-Cyl 94.91 
Chevrolet Suburban 4-door Yellow 8-Cyl 204.92 
Nissan Pathfinder 4-door White 6-Cyl 145.11 
Chevrolet Spark 2-door Teal 4-Cyl 99.55

А вот более безопасный способ прочитать:

#define STR_SIZE 12

char  make[STR_SIZE]={""}, model[STR_SIZE]={""}, size[STR_SIZE]={""}, color[STR_SIZE]={""},
        power[STR_SIZE]={""};
float daily_rate;

fscanf(cfPtr, "%*[^\n]");
while (fscanf(cfPtr, "%11s%11s%11s%11s%11s%f", make, model, size, color, power, &daily_rate) == 6) { 
    add_at_end(make, model, size, color, power, daily_rate, 'A');
}

Поля %11s будут считывать до 11 символов в ваших 12-символьных массивах, оставляя место для ограничителя строки, который fscanf() будет добавлять к каждому. Это по-прежнему будет иметь проблемы, если в файле слишком длинные данные, но это не должно вызывать ошибки.

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

0 голосов
/ 29 апреля 2019

Вот несколько наблюдений.

Во-первых, обеспечит большую читабельность вашего кода, дав некоторое осмысленное имя макроса вместо TWELVE.Например, #define NUMBER_OF_ITEMS 12

Во-вторых , В определении функции формальный аргумент make[TWELVE] не выглядит хорошо, когда вы передаете массив символов и он распадается на char*, поэтому достаточно только char *make.Этот

void add_at_end(char make[TWELVE], char model[TWELVE], char size[TWELVE], char color[TWELVE], char power[TWELVE], float daily_rate, char rented) { }

может быть заменен на

void add_at_end(char *make, char *model, char *size, char *color, char *power, float daily_rate, char rented) { }

И , что наиболее важно , это

fscanf(cfPtr, "%s%s%s%s%s%s", make, model, size, color, power, dAilyRate); /* Just Remove it */

непосредственно перед

while (!feof(cfPtr)) 

создает проблему, т. Е. Информацию, которую вы не используете, она перезаписывается вторым оператором fscanf() внутри цикла.Также прочитайте Почему «while (! Feof (file))» всегда неверно?

Пример кода

void add_at_end(char *make, char *model, char *size, char *color, char *power, float daily_rate, char rented)
{
   /* same code */
}

И

int ReadFileStoreInList(void)
{
    FILE *cfPtr;

    if ((cfPtr = fopen("input", "r")) !=NULL)
    {
        char  make[TWELVE]={""}, model[TWELVE]={""}, size[TWELVE]={""}, color[TWELVE]={""}, power[TWELVE]={""};
        char rented = 'A';
        float daily_rate=0.0;
        char dAilyRate[TWELVE];
        while (fscanf(cfPtr, "%s%s%s%s%s%f", make, model, size, color, power, &daily_rate) == 6)
        {
            rented = 'A';
            add_at_end(make, model, size, color, power, daily_rate, rented);
        }
        printScreenTitleAndHeaderForCars();
        traverse_in_order();
    }
    else
    {
        puts("Input data file could not be opened, I have no new inventory of cars from headquarters\n\n\n");
        return 0; /* in case of fopen failed */
    }
    fclose(cfPtr);
}
...