Реализация динамического выделения памяти в файлах в программе C

#c

Вопрос:

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

Приведенная ниже программа получает данные от пользователя и сохраняет их в файле.

 #include <stdio.h>
#include <stdlib.h>
 
typedef struct
{
    char name[10];
    char nameEr[10];
    int salaryannual;
    char desig[10];
 
}Employee;
 
void main(int argc, char *argv[])
{
    int i,n=0,*ptr,sum=0;
    n=atoi(argv[1]);
    printf("The no.of employee records to be created are: %dn",n);
    
    FILE *fp;
    
    if((fp=fopen("employee.txt","w"))==NULL) 
    {
      printf("File could not be openedn");
    } 
    
 
    Employee employees[n];
 
    printf("Enter %d Employee Details: n n",n);
    
    for(i=0; i<n; i  )
    {
 
        printf("Employee %d: n",i 1);
        printf("Employee name: ");
        scanf("%s",employees[i].name);
        printf("Employer name: ");
        scanf("%s",employees[i].nameEr);
        printf("Employee designation: ");
        scanf("%s",employees[i].desig);
        printf("Employee annual salary: ");
        scanf("%d",amp;employees[i].salaryannual);
        
 
        printf("n");
    }
 
    for(i=0;i<n;i  )
    {
      fprintf(fp,"%sn",employees[i].name);
      fprintf(fp,"%sn",employees[i].nameEr);
      fprintf(fp,"%sn",employees[i].desig);
      fprintf(fp,"%dn",employees[i].salaryannual);
    }

    fclose(fp);
 
    for(i=0;i<n;i  )
    {
 
        printf("Employee name t: ");
        printf("%s n",employees[i].name);
 
        printf("Employer name t: ");
        printf("%s n",employees[i].nameEr);
        
        printf("Employee designation t: ");
        printf("%s n",employees[i].desig);
 
        printf("Employee annual salary: ");
        printf("%d n",employees[i].salaryannual);
        
 
        printf("n");
    }
 

Как мне поступить дальше с этим кодом.
Пожалуйста, помогите мне.

Комментарии:

1. Подсказка: если файл не удалось открыть printf("File could not be openedn"); , этого недостаточно

Ответ №1:

Безусловно, существует множество подходов к решению этой проблемы.
Я думаю, что один из самых простых-это:

  1. Получите количество сотрудников, открыв файл для чтения и подсчитав количество строк. Количество сотрудников будет равно количеству строк, разделенных на четыре, так как на каждого сотрудника приходится четыре строки. Обычно для этого вы создаете функцию:
 int get_number_of_employees(int *number_of_employees)
{
    int ret = -1;

    int line_count = 0;

    FILE *fp = NULL;
    if ((fp = fopen("employee.txt", "r")) != NULL) {

        char line[64]  = {0,};
        while(fgets(line, sizeof(line), fp)) {

            line_count  ;
        }

        *number_of_employees = line_count / 4;
        ret = 0;

        fclose(fp);
    }

    return ret;
}
 
  1. Выделите память для своего буфера, используя количество сотрудников, которых вы получили с помощью функции выше:
 int salaries_array[employees_count];
 
  1. Создайте функцию для заполнения только что созданного массива. Для этого потребуется, чтобы программа открыла файл для повторного чтения:
 int get_employees_salaries(int salaries_array[], int employees_count)
{
    int ret = -1;

    int line_count = 0;

    FILE *fp = NULL;
    if ((fp = fopen("employee.txt", "r")) != NULL) {

        char line[64]  = {0,};
        while(fgets(line, sizeof(line), fp)) {

            line_count  ;
            if ((line_count % 4) == 0) {

                int salary = atoi(line);
                salaries_array[(line_count / 4) - 1] = salary;
                ret = 0;
            }
        }

        fclose(fp);
    }

    return ret;
}
 
  1. Теперь создайте функцию для получения средней заработной платы из массива:
 int get_average_salary(int salaries_array[], int employees_count)
{
    int salaries_sum = 0;

    int i = 0;
    for (; i < employees_count; i  ) {

        salaries_sum  = salaries_array[i];
    }

    return salaries_sum / employees_count;
}
 

Складываю все это воедино:

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
    char name[10];
    char nameEr[10];
    int salaryannual;
    char desig[10];

} Employee;

int get_number_of_employees(int *number_of_employees)
{
    int ret = -1;

    int line_count = 0;

    FILE *fp = NULL;
    if ((fp = fopen("employee.txt", "r")) != NULL) {

        char line[64]  = {0,};
        while(fgets(line, sizeof(line), fp)) {

            line_count  ;
        }

        *number_of_employees = line_count / 4;
        ret = 0;

        fclose(fp);
    }

    return ret;
}

int get_employees_salaries(int salaries_array[], int employees_count)
{
    int ret = -1;

    int line_count = 0;

    FILE *fp = NULL;
    if ((fp = fopen("employee.txt", "r")) != NULL) {

        char line[64]  = {0,};
        while(fgets(line, sizeof(line), fp)) {

            line_count  ;
            if ((line_count % 4) == 0) {

                int salary = atoi(line);
                salaries_array[(line_count / 4) - 1] = salary;
                ret = 0;
            }
        }

        fclose(fp);
    }

    return ret;
}

int get_average_salary(int salaries_array[], int employees_count)
{
    int salaries_sum = 0;
    int i = 0;
    for (; i < employees_count; i  ) {

        salaries_sum  = salaries_array[i];
    }
    return salaries_sum / employees_count;
}

void main (int argc, char *argv[])
{
    int employees_count = 0;
    if (get_number_of_employees(amp;employees_count) == 0) {

        int salaries_array[employees_count];
        if (get_employees_salaries(salaries_array, employees_count) == 0) {

            int average_salary = get_average_salary(salaries_array, employees_count);
            printf("Average salary = %dn", average_salary);
        }
    }
}
 

Комментарии:

1. Спасибо вам за ваш ответ! @JardelLucca. Действительно полезно. Есть идея, как использовать функцию в этой программе.

2. Функции великолепны, но в случае, если они слишком увеличиваются в размерах, рекомендуется разделить их на более мелкие функции.

Ответ №2:

Для динамического распределения сначала необходимо определиться со схемой распределения. Вот несколько схем:

  • увеличивайте каждый раз на фиксированное число
  • увеличение на фиксированный процент

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

 /* Increment by fixed number */
int increment = 5;
int employee_count = 0;
int employee_max = increment;
Employee* employees = malloc(sizeof(Employee) * employee_max);

...
void Save(Employee* emp)
{
    /* check if there is enough space */
    if (employee_count == employee_max)
    {
         employee_max  = increment;
         employees = realloc(employees, sizeof(Employee) * employee_max)
    }
    employees[employee_count] = *emp;
      employee_count;
}
 

Если у вас 1000 записей, этот метод перераспределяется 200 раз. Альтернативой является увеличение на процент

 int increment_pc = 150; /* 50% */
int employee_count = 0;
int employee_max = 10;
Employee* employees = malloc(sizeof(Employee) * employee_max);

...
void Save(Employee* emp)
{
    /* check if there is enough space */
    if (employee_count == employee_max)
    {
         employee_max = (employee_max * increment_pc) / 100;
         employees = realloc(employees, sizeof(Employee) * employee_max)
    }
    employees[employee_count] = *emp;
      employee_count;
}
 

Используя эту схему распределения, 1000 сотрудников получают 13 перераспределений, но есть некоторые потери. Последнее распределение выделит 1234, что на 234 больше. В предыдущей схеме это будет, самое большее, 4 раза больше. Это просто проблема пространства-времени.

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

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

Комментарии:

1. Спасибо за ваш ответ @cup. Воплотю вашу концепцию в моей программе!

2. Это отличный анализ и краткое изложение ключевых моментов в решении проблемы. Я даже не думал о возможных результатах распределения памяти по частям. Только хороший/опытный программист мог бы легко перейти на этот уровень. Еще одним моментом в моем коде (а также в коде операции) является тот факт, что память неизвестного размера выделяется в стеке. Для любого приложения в реальной жизни это должно быть рассмотрено!