Хорошо, я получил ваш комментарий, но давайте попробуем массив struct , чтобы сделать вашу обработку данных управляемой. Это не тривиальное дополнение к вашему последнему вопросу. Попытка согласовать 5 отдельных массивов (3 из которых были бы двумерными символьными массивами) создает больше проблем, чем стоит. struct
- это правильный способ координировать различные типы информации как одну единицу. Кроме того, если у вас нет постоянной необходимости использовать bool
, просто используйте int
. Компилятор может обрабатывать собственный тип int
так же эффективно.
Давайте начнем с вашей структуры. struct
- это не более чем удобная оболочка, позволяющая собирать разные типы и обрабатывать их как один объект. В вашем случае ваши строки символов name, plate, date
, ваши int type
и ваши double value
(ваши amount
) могут быть членами struct
. Вы можете использовать typedef
для структуры, чтобы сделать ее использование более удобным, поэтому вместо написания struct nameofstruct
везде вы можете просто использовать typedeffed nameofstruct
, как если бы вы использовали обычный тип, такой как int
. Например:
#define MAXS 16u /* max number of structs */
#define MAXNM 32u /* max characters in name and other arrays in struct */
#define MAXC 1024u /* max characters in read buffer */
#define SECPY 31536000u /* seconds per-year */
typedef struct mydata {
char name[MAXNM],
plate[MAXNM],
date[MAXNM];
int type;
double value;
} mydata_t;
Создайте struct mydata
с typedef
из mydata_t
, который является ссылкой (псевдонимом) на struct mydata
(вы можете опустить mydata
и просто ввести определение анонимной структуры).
Теперь вы можете создать массив struct mydata
или просто mydata_t
, и каждый элемент массива является структурой, которая может содержать каждое из этих значений.
Теперь давайте начнем с вашей проблемы. Как упоминалось выше, просто объявите массив mydata_t
вместо 5 отдельных массивов, например,
int main (int argc, char **argv) {
...
int n = 0, ndx = 0; /* NOTE when dealing with array, start at ZERO */
...
mydata_t data[MAXS] = {{ .name = "" }};
Теперь у вас есть массив 16
struct для работы (отрегулируйте свою константу, определяя размер массива по мере необходимости), и у вас есть 2 счетчика n
, чтобы отслеживать, какой элемент в вашей структуре вы сейчас читаете, и ndx
индекс структуры в массиве, который вы заполняете.
Используя их, вы можете прочитать каждую строку, как в своем последнем вопросе, и использовать n
, чтобы определить, что делать со строкой (вы можете использовать isspace()
, чтобы проверить, является ли первый символ белой строкой (включая '\n'
). ), чтобы пропустить пустые строки. Затем просто передайте n
на switch(n)
, чтобы выполнить соответствующее действие для этой строки (или использовать набор if ... else if ... else if ...
) Например:
while (fgets (buf, MAXC, fp)) { /* read each line in file */
if (isspace(*buf)) /* skip blank lines (or start with space) */
continue;
buf[strcspn (buf, "\r\n")] = 0; /* trim '\n' from end of buf */
/* if line isn't a date line, just output line as non-date line */
switch (n) {
case 0: /* fill the name in struct */
case 1: /* fill the plate in struct */
case 2: /* set the type in struct */
...
( примечание: buf[strcspn (buf, "\r\n")] = 0;
- это просто удобный способ обрезки '\n'
(или \r\n
) от конца буфера до его копирования в name, plate, date
и т. Д. )
В каждом из ваших случаев вы просто ВАЛИДИРУЕТЕ, что строка, прочитанная с помощью fgets
, поместится в строковый член вашей структуры, прежде чем скопировать его, или вы преобразуете строку в нужное вам числовое значение, например:
case 0:
if (strlen (buf) < MAXNM)
strcpy (data[ndx].name, buf);
else {
fputs ("error: name exceeds storage.\n", stderr);
exit (EXIT_FAILURE);
}
n++; /* advance n counter to act on next member */
break;
или
case 2:
if (sscanf (buf, "%d", &data[ndx].type) != 1) {
fputs ("error: type not an integer.\n", stderr);
exit (EXIT_FAILURE);
}
n++;
break;
(Вы используете оператор '.'
(точка) для доступа к каждому члену структуры при работе с самой структурой, или вы используете оператор '->'
(стрелка), если у вас есть указатель на struct. Когда у вас есть массив stuct, индекс массива ([..]
) действует как разыменование. Точно так же, как массив чего-либо еще)
Так как вы не сохраняете year, month, day
, вам просто нужна функция, которая проверяет время с этого момента, чтобы вы могли проверить, прошло ли оно больше года. Таким образом, вы просто передаете свою дату в функцию и получаете время в секундах назад как double
, например,
double check_time_from_now (const char *str)
{
int y, m, d;
if (sscanf (str, "%4d%2d%2d", &y, &m, &d) != 3) {
fprintf (stderr, "error non-date string: '%s'.\n", str);
exit (EXIT_FAILURE);
}
time_t now = time(NULL),
then = fill_broken_down_time (y, m, d);
double secs = difftime (now, then); /* get seconds between dates */
return secs;
}
Другая простая функция позволяет вам печатать массив структур, например,
void prn_data_t_array (mydata_t *data, int n)
{
for (int i = 0; i < n; i++)
printf ("%-12s %-8s %d %9.2f %s\n", data[i].name, data[i].plate,
data[i].type, data[i].value, data[i].date);
}
С этим мы можем взглянуть на остаток вашего switch(n)
, который показывает, как обрабатывать каждый случай:
while (fgets (buf, MAXC, fp)) { /* read each line in file */
if (isspace(*buf)) /* skip blank lines (or start with space) */
continue;
buf[strcspn (buf, "\r\n")] = 0; /* trim '\n' from end of buf */
/* if line isn't a date line, just output line as non-date line */
switch (n) {
case 0:
if (strlen (buf) < MAXNM)
strcpy (data[ndx].name, buf);
else {
fputs ("error: name exceeds storage.\n", stderr);
exit (EXIT_FAILURE);
}
n++;
break;
case 1:
if (strlen (buf) < MAXNM)
strcpy (data[ndx].plate, buf);
else {
fputs ("error: plate exceeds storage.\n", stderr);
exit (EXIT_FAILURE);
}
n++;
break;
case 2:
if (sscanf (buf, "%d", &data[ndx].type) != 1) {
fputs ("error: type not an integer.\n", stderr);
exit (EXIT_FAILURE);
}
n++;
break;
case 3:
if (sscanf (buf, "%lf", &data[ndx].value) != 1) {
fputs ("error: value not a double.\n", stderr);
exit (EXIT_FAILURE);
}
n++;
break;
case 4:
if (strlen (buf) < MAXNM)
strcpy (data[ndx].date, buf);
else {
fputs ("error: date exceeds storage.\n", stderr);
exit (EXIT_FAILURE);
}
if (check_time_from_now (data[ndx].date) > SECPY) {
if (data[ndx].type)
data[ndx].value *= 1.5;
else
data[ndx].value *= 2.5;
}
n = 0;
ndx++;
if (ndx == MAXS)
goto arrayfull;
break;
default:
fputs ("error: you shouldn't get here!\n", stderr);
break;
}
}
( примечание: присмотритесь к case 4:
и тому, как счетчик строк n
сбрасывается в ноль и как увеличивается индекс вашего массива ndx
, чтобы вы затем заполняли следующую структуру в своем массиве Также note вы защищаете границы массива, проверяя if (ndx == MAXS)
, чтобы убедиться, что вы не пишете больше структур, чем в массиве.)
Если сложить все вместе, вы получите:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#define MAXS 16u /* max number of structs */
#define MAXNM 32u /* max characters in name and other arrays in struct */
#define MAXC 1024u /* max characters in read buffer */
#define SECPY 31536000u /* seconds per-year */
typedef struct {
char name[MAXNM],
plate[MAXNM],
date[MAXNM];
int type;
double value;
} mydata_t;
time_t fill_broken_down_time (int y, int m, int d)
{ /* initialize struct members */
struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d,
.tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };
return mktime(&bdt); /* return mktime conversion to time_t */
}
double check_time_from_now (const char *str)
{
int y, m, d;
if (sscanf (str, "%4d%2d%2d", &y, &m, &d) != 3) {
fprintf (stderr, "error non-date string: '%s'.\n", str);
exit (EXIT_FAILURE);
}
time_t now = time(NULL),
then = fill_broken_down_time (y, m, d);
double secs = difftime (now, then); /* get seconds between dates */
return secs;
}
void prn_data_t_array (mydata_t *data, int n)
{
for (int i = 0; i < n; i++)
printf ("%-12s %-8s %d %9.2f %s\n", data[i].name, data[i].plate,
data[i].type, data[i].value, data[i].date);
}
int main (int argc, char **argv) {
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
int n = 0, ndx = 0; /* NOTE when dealing with array, start at ZERO */
char buf[MAXC]; /* buffer to hold each line read from file */
mydata_t data[MAXS] = {{ .name = "" }};
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line in file */
if (isspace(*buf)) /* skip blank lines (or start with space) */
continue;
buf[strcspn (buf, "\r\n")] = 0; /* trim '\n' from end of buf */
/* if line isn't a date line, just output line as non-date line */
switch (n) {
case 0:
if (strlen (buf) < MAXNM)
strcpy (data[ndx].name, buf);
else {
fputs ("error: name exceeds storage.\n", stderr);
exit (EXIT_FAILURE);
}
n++;
break;
case 1:
if (strlen (buf) < MAXNM)
strcpy (data[ndx].plate, buf);
else {
fputs ("error: plate exceeds storage.\n", stderr);
exit (EXIT_FAILURE);
}
n++;
break;
case 2:
if (sscanf (buf, "%d", &data[ndx].type) != 1) {
fputs ("error: type not an integer.\n", stderr);
exit (EXIT_FAILURE);
}
n++;
break;
case 3:
if (sscanf (buf, "%lf", &data[ndx].value) != 1) {
fputs ("error: value not a double.\n", stderr);
exit (EXIT_FAILURE);
}
n++;
break;
case 4:
if (strlen (buf) < MAXNM)
strcpy (data[ndx].date, buf);
else {
fputs ("error: date exceeds storage.\n", stderr);
exit (EXIT_FAILURE);
}
if (check_time_from_now (data[ndx].date) > SECPY) {
if (data[ndx].type)
data[ndx].value *= 1.5;
else
data[ndx].value *= 2.5;
}
n = 0;
ndx++;
if (ndx == MAXS)
goto arrayfull;
break;
default:
fputs ("error: you shouldn't get here!\n", stderr);
break;
}
}
arrayfull:;
if (fp != stdin) fclose (fp); /* close file if not stdin */
puts ("\ncomputed information\n");
prn_data_t_array (data, ndx); /* print the computed values */
return 0;
}
Теперь используя ваш файл данных:
Пример входного файла
$ cat dat/namelbltypevaldate.txt
Hanif Hefaz
BA123HB
0
100.50
20180101
Jacki Shroff
UP673MK
1
3000.99
20170512
Пример использования / Вывод
Код выдаст:
$ ./bin/time_from_now3 dat/namelbltypevaldate.txt
computed information
Hanif Hefaz BA123HB 0 251.25 20180101
Jacki Shroff UP673MK 1 4501.48 20170512
Если вы проверите, соответствующий коэффициент был применен к каждому.
Я действительно не хотел бы делать это с 5 отдельными массивами.Структура является подходящим инструментом для работы.Вы можете использовать 5 массивов, точно так же, как я использовал вышеупомянутый массив struct - у вас просто есть гораздо более длинный список переменных и намного больше имен, которые нужно отслеживать в вашем коде.Просмотрите это и дайте мне знать, если у вас есть еще вопросы.
Динамически выделяемый массив структур
Функционально программе не важно, где находится хранилище для ваших данных.является.Однако выделение с помощью автоматического хранения имеет недостаток, заключающийся в невозможности динамического увеличения при продолжении добавления записей.Альтернатива для динамического распределения ваших данных с использованием malloc, calloc, realloc
.Это добавляет небольшую дополнительную сложность, поскольку теперь вам нужно отслеживать доступное выделенное хранилище, используемое хранилище и перераспределять, когда используемое хранилище равно вашему доступному хранилищу.
Вы не хотите realloc
каждая строка - это неэффективно.Вместо этого вы будете выделять некоторое разумное начальное количество структур, заполнять их, пока не достигнете предела, а затем перераспределить.Сколько вы перераспределяете, зависит от вас, но общие схемы перераспределения заключаются в удвоении текущего выделенного размера (или вы можете добавить другое кратное значение, например 3/2
и т. Д., В зависимости от того, насколько быстро вы хотите увеличить свое распределение).В этом примере используется старый добрый метод double.
Сначала изменения (для печати только строк, длина которых превышает 1 год) просто включают перемещение вашего результата в:
if (check_time_from_now (data[ndx].date) > SECPY) {
Добавление функциидля вывода записи ndx
это то, что вы можете сделать, чтобы сохранить тело вашего кода в чистоте, например,
void prn_data_t_rec (mydata_t *data, int n)
{
printf ("%-12s %-8s %d %9.2f %s\n", data[n].name, data[n].plate,
data[n].type, data[n].value, data[n].date);
}
, что делает вызов из тела вашего кода:
if (check_time_from_now (data[ndx].date) > SECPY) {
if (data[ndx].type)
data[ndx].value *= 1.5;
else
data[ndx].value *= 2.5;
prn_data_t_rec (data, ndx); /* output > 1 year */
}
Теперьк динамическому распределению.Вместо объявления:
mydata_t data[MAXS] = {{ .name = "" }};
Вы просто измените объявление и начальное выделение на:
#define MAXS 16u /* initial number of structs */
...
int maxs = MAXS; /* variable to track allocated number of struct */
mydata_t *data = calloc (maxs, sizeof *data); /* allocate storage */
( note: calloc
было выбрано вместо malloc
потому что я хочу инициализировать ноль всей памяти и избежать необходимости явного инициализатора, чтобы установить каждый элемент type
или value
на ноль в противном случае. Затраты на calloc
для выполнения этого незначительны)
Теперь * Подтвердите КАЖДОЕ распределение
if (!data) { /* validate every allocation */
perror ("calloc-data");
return 1;
}
То же самое относится к каждому перераспределению с оговоркой.Вы Всегда realloc
с временным указателем .Если вы перераспределите с помощью самого указателя, e..g ptr = realloc (ptr, newsize);
и realloc
не вернут NULL
- вы перезапишете свой первоначальный адрес с NULL
, создав утечку памяти и потеряв весь доступ к вашим существующим данным!
Все остальное в коде идентично, пока вы не доберетесь до realloc
в case 4:
из switch()
.Там, когда (ndx == maxs)
вы должны realloc
дополнительное хранилище, например,
case 4:
if (strlen (buf) < MAXNM)
strcpy (data[ndx].date, buf);
else {
fputs ("error: date excceeds storage.\n", stderr);
exit (EXIT_FAILURE);
}
if (check_time_from_now (data[ndx].date) > SECPY) {
if (data[ndx].type)
data[ndx].value *= 1.5;
else
data[ndx].value *= 2.5;
prn_data_t_rec (data, ndx);
}
n = 0;
ndx++;
if (ndx == maxs) { /* check if realloc required */
/* realloc w/temp pointer to 2X current size */
void *tmp = realloc (data, 2 * maxs * sizeof *data);
if (!tmp) { /* validate every allocation */
perror ("realloc-data");
goto outofmem; /* original data still good */
}
data = tmp; /* set data to newly reallocated block */
/* zero the new memory allocated (optional) */
memset (data + maxs, 0, maxs * sizeof *data);
maxs *= 2; /* update the currently allocated max */
}
break;
( примечание: продолжение использования goto
- здесь необходимо сохранить ваш оригинал data
в случае сбоя realloc
. Даже в случае сбоя ВСЕ исходное data
по-прежнему исправно - и поскольку вы использовали временный указатель для проверки перераспределения, вы не потеряли к нему доступ.Так что просто выпрыгните из переключателя и цикла while
, и вы сможете продолжать использовать его и free
, когда он больше не нужен.
Собрав весь пример, вы получите:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#define MAXS 16u /* initial number of structs */
#define MAXNM 32u /* max characters in name and other arrays in struct */
#define MAXC 1024u /* max characters in read buffer */
#define SECPY 31536000u /* seconds per-year */
typedef struct {
char name[MAXNM],
plate[MAXNM],
date[MAXNM];
int type;
double value;
} mydata_t;
time_t fill_broken_down_time (int y, int m, int d)
{ /* initialize struct members */
struct tm bdt = { .tm_sec=0, .tm_min=0, .tm_hour=0, .tm_mday=d,
.tm_mon=m>0?m-1:0, .tm_year=y-1900, .tm_isdst=-1 };
return mktime(&bdt); /* return mktime conversion to time_t */
}
double check_time_from_now (const char *str)
{
int y, m, d;
if (sscanf (str, "%4d%2d%2d", &y, &m, &d) != 3) {
fprintf (stderr, "error non-date string: '%s'.\n", str);
exit (EXIT_FAILURE);
}
time_t now = time(NULL),
then = fill_broken_down_time (y, m, d);
double secs = difftime (now, then); /* get seconds between dates */
return secs;
}
void prn_data_t_array (mydata_t *data, int n)
{
for (int i = 0; i < n; i++)
printf ("%-12s %-8s %d %9.2f %s\n", data[i].name, data[i].plate,
data[i].type, data[i].value, data[i].date);
}
void prn_data_t_rec (mydata_t *data, int n)
{
printf ("%-12s %-8s %d %9.2f %s\n", data[n].name, data[n].plate,
data[n].type, data[n].value, data[n].date);
}
int main (int argc, char **argv) {
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
int n = 0, ndx = 0, /* NOTE when dealing with array, start at ZERO */
maxs = MAXS; /* variable to track allocated number of struct */
char buf[MAXC]; /* buffer to hold each line read from file */
mydata_t *data = calloc (maxs, sizeof *data); /* allocate storage */
if (!data) { /* validate every allocation */
perror ("calloc-data");
return 1;
}
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
puts ("\ncomputed information for records > 1 year from now\n");
while (fgets (buf, MAXC, fp)) { /* read each line in file */
if (isspace(*buf)) /* skip blank lines (or start with space) */
continue;
buf[strcspn (buf, "\r\n")] = 0; /* trim '\n' from end of buf */
/* if line isn't a date line, just output line as non-date line */
switch (n) {
case 0:
if (strlen (buf) < MAXNM)
strcpy (data[ndx].name, buf);
else {
fputs ("error: name excceeds storage.\n", stderr);
exit (EXIT_FAILURE);
}
n++;
break;
case 1:
if (strlen (buf) < MAXNM)
strcpy (data[ndx].plate, buf);
else {
fputs ("error: plate excceeds storage.\n", stderr);
exit (EXIT_FAILURE);
}
n++;
break;
case 2:
if (sscanf (buf, "%d", &data[ndx].type) != 1) {
fputs ("error: type not an integer.\n", stderr);
exit (EXIT_FAILURE);
}
n++;
break;
case 3:
if (sscanf (buf, "%lf", &data[ndx].value) != 1) {
fputs ("error: value not a double.\n", stderr);
exit (EXIT_FAILURE);
}
n++;
break;
case 4:
if (strlen (buf) < MAXNM)
strcpy (data[ndx].date, buf);
else {
fputs ("error: date excceeds storage.\n", stderr);
exit (EXIT_FAILURE);
}
if (check_time_from_now (data[ndx].date) > SECPY) {
if (data[ndx].type)
data[ndx].value *= 1.5;
else
data[ndx].value *= 2.5;
prn_data_t_rec (data, ndx);
}
n = 0;
ndx++;
if (ndx == maxs) { /* check if realloc required */
/* realloc w/temp pointer to 2X current size */
void *tmp = realloc (data, 2 * maxs * sizeof *data);
if (!tmp) { /* validate every allocation */
perror ("realloc-data");
goto outofmem; /* original data still good */
}
data = tmp; /* set data to newly reallocated block */
/* zero the new memory allocated (optional) */
memset (data + maxs, 0, maxs * sizeof *data);
maxs *= 2; /* update the currently allocated max */
}
break;
default:
fputs ("error: you shouldn't get here!\n", stderr);
break;
}
}
outofmem:;
if (fp != stdin) fclose (fp); /* close file if not stdin */
puts ("\nall stored information\n");
prn_data_t_array (data, ndx); /* print the computed values */
free (data); /* don't forget to free what you allocate */
return 0;
}
( примечание: печатать > 1 year
во время цикла и выводить итоговые записи в конце - корректировать по мере необходимости)
Пример Использование / Вывод
Использование того же файла данных:
$ ./bin/time_from_now4 dat/namelbltypevaldate.txt
computed information for records > 1 year from now
Hanif Hefaz BA123HB 0 251.25 20180101
Jacki Shroff UP673MK 1 4501.48 20170512
all stored information
Hanif Hefaz BA123HB 0 251.25 20180101
Jacki Shroff UP673MK 1 4501.48 20170512
Использование памяти / проверка ошибок
В любом написанном вами коде, который динамически выделяет памятьу вас есть 2 обязанностей в отношении любого выделенного блока памяти: (1) всегда нажмитеуказатель на начальный адрес для блока памяти, поэтому (2) он может быть освобожден , когда он больше не нужен.
Обязательно, чтобы вы использовали программу проверки ошибок памяти, чтобы гарантировать, что вы не пытаетесь получить доступ к памяти или писать за пределами / за пределами выделенного блока, пытаться прочитать или основать условный переход на неинициализированном значении и, наконец, , чтобы подтвердить, что вы освобождаете всю выделенную память.
Для Linux valgrind
- нормальный выбор. Для каждой платформы есть похожие проверки памяти. Все они просты в использовании, просто запустите вашу программу через него.
$ ./bin/time_from_now4 dat/namelbltypevaldate.txt
==4331== Memcheck, a memory error detector
==4331== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==4331== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==4331== Command: ./bin/time_from_now4 dat/namelbltypevaldate.txt
==4331==
computed information for records > 1 year from now
Hanif Hefaz BA123HB 0 251.25 20180101
Jacki Shroff UP673MK 1 4501.48 20170512
all stored information
Hanif Hefaz BA123HB 0 251.25 20180101
Jacki Shroff UP673MK 1 4501.48 20170512
==4331==
==4331== HEAP SUMMARY:
==4331== in use at exit: 0 bytes in 0 blocks
==4331== total heap usage: 12 allocs, 12 frees, 5,333 bytes allocated
==4331==
==4331== All heap blocks were freed -- no leaks are possible
==4331==
==4331== For counts of detected and suppressed errors, rerun with: -v
==4331== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.
Дайте мне знать, если у вас есть дополнительные вопросы (что, вероятно, потребует нового вопроса на этом этапе)