Кажется, что большая часть трудностей, с которыми вы сталкиваетесь, пытаясь понять, как использовать "TN"
, связана с вашей попыткой сохранить все данные, считанные в каждой строке, в отдельной структуре.Как упомянуто в комментариях, это может быть хорошо для чтения данных в базу данных, где база данных предоставляет возможность запрашивать все записи с помощью аббревиатуры состояний, но делает обработку данных немного более неудобной.Почему?
Когда вы сохраняете все записи как отдельную структуру, нет никакой связи между состоянием, к которому принадлежат данные, и информацией, хранящейся, кроме члена code
структуры.Это означает, что если вы хотите найти или распечатать информацию, например, "TN"
, вы должны пройти через каждую отдельную структуру, проверяя, соответствует ли элемент code
"TN"
.Подумайте о печати.Вы должны зацикливаться для каждого состояния, а затем зацикливаться на каждой структуре каждый раз, выбирая информацию для отдельного состояния для печати.
Вместо сохранения каждой записи информации в качестве элемента в массиве записей, почему бы и нетиметь массив состояний, где каждое состояние содержит указатель на данные для этого состояния.Это сделало бы ваш num_records
член более понятнымПосле этого вам просто придется перебрать свой массив состояний, проверить, является ли (num_records > 0)
, а затем распечатать информацию num_records
для этого состояния, пропуская все состояния, в которых данные не были сохранены.Это обеспечивает гораздо более эффективный подход.
Например, требуется немного усилий, чтобы немного изменить ваши структуры, чтобы обеспечить связь между состоянием и данными, связанными с этим состоянием, например:
#include <stdio.h>
#include <stdlib.h>
/* if you need constants, either #define them or use an enum */
enum { ABRV = 2, NDATA = 8, LOC = 13, NAME = 15, MAXC = 1024 };
...
typedef struct { /* struct holding only climate data */
long long millitime;
char location[LOC];
double humidity;
double snow;
double cloud;
double lightning;
long double pressure;
double temperature;
} climate_t;
typedef struct {
size_t num_allocated, /* track of how many data are allocated */
num_records;
climate_t *data; /* a pointer to allocated block for data */
} statedata_t;
Но как соотнести чтение "TN"
из файла, чтобы получить данные, хранящиеся в правильном состоянии?Вот где приходит таблица поиска. Если бы у вас была другая простая структура, содержащая имя и аббревиатуру состояния, вы могли бы создать простой массив структуры, содержащий информацию об аббревиатуре, и когда вы читаете, например, "TN"
из файла, вы можетепросто "посмотрите" индекс , где "TN"
живет в вашем массиве с сокращениями, а затем используйте этот index , чтобы сохранить информацию из этой строки в соответствующем индексе в вашем statedata_t
массиве.
Поскольку ваш "lookup-array" будет постоянным, он может быть просто глобальным, который объявлен как const
.Если вы используете несколько исходных файлов, вы можете просто определить массив в одном файле и объявить его extern
в остальных файлах, которые в нем нуждаются.Так как бы вы это определили?Сначала объявите строку с нужной вам информацией в поиске (имя и аббревиатуру состояния), а затем объявите их постоянный массив, инициализирующий имя и аббревиатуру для каждого, например,
typedef struct {
char name[NAME+1],
abrv[ABRV+1];
} stateabrv_t;
...
const stateabrv_t state[] = { { "Alabama", "AL" },
{ "Alaska", "AK" },
{ "Arizona", "AZ" },
{ "Arkansas", "AR" },
{ "California", "CA" },
{ "Colorado", "CO" },
{ "Connecticut", "CT" },
{ "Delaware", "DE" },
{ "Florida", "FL" },
{ "Georgia", "GA" },
{ "Hawaii", "HI" },
{ "Idaho", "ID" },
{ "Illinois", "IL" },
{ "Indiana", "IN" },
{ "Iowa", "IA" },
{ "Kansas", "KS" },
{ "Kentucky", "KY" },
{ "Louisiana", "LA" },
{ "Maine", "ME" },
{ "Maryland", "MD" },
{ "Massachusetts", "MA" },
{ "Michigan", "MI" },
{ "Minnesota", "MN" },
{ "Mississippi", "MS" },
{ "Missouri", "MO" },
{ "Montana", "MT" },
{ "Nebraska", "NE" },
{ "Nevada", "NV" },
{ "New Hampshire", "NH" },
{ "New Jersey", "NJ" },
{ "New Mexico", "NM" },
{ "New York", "NY" },
{ "North Carolina", "NC" },
{ "North Dakota", "ND" },
{ "Ohio", "OH" },
{ "Oklahoma", "OK" },
{ "Oregon", "OR" },
{ "Pennsylvania", "PA" },
{ "Rhode Island", "RI" },
{ "South Carolina", "SC" },
{ "South Dakota", "SD" },
{ "Tennessee", "TN" },
{ "Texas", "TX" },
{ "Utah", "UT" },
{ "Vermont", "VT" },
{ "Virginia", "VA" },
{ "Washington", "WA" },
{ "West Virginia", "WV" },
{ "Wisconsin", "WI" },
{ "Wyoming", "WY" } };
const int nstates = sizeof state / sizeof *state;
Теперь у вас есть простойДвухсторонний поиск.Учитывая имя или сокращение состояния, вы можете вернуть index , где оно находится в массиве.Кроме того, по имени вы можете искать аббревиатуру или по аббревиатуре, вы можете искать имя.
Простая функция поиска, возвращающая индекс, может выглядеть так:
/* simple lookup function, given a code s, return index for state
* in array of statedata_t on success, -1 otherwise.
*/
int lookupabrv (const char *s)
{
int i = 0;
for (; i < nstates; i++)
if (state[i].abrv[0] == s[0] && state[i].abrv[1] == s[1])
return i;
return -1;
}
Теперь, когда выВы можете найти index с учетом сокращения, используя вашу глобальную таблицу поиска, вы можете собрать остаток вашей обработки данных в main()
, объявив массив 50 statedata_t
, например,
int main (int argc, char **argv) {
char buf[MAXC]; /* line buffer */
/* array of 50 statedata_t (one for each state) */
statedata_t stdata[sizeof state / sizeof *state] = {{.num_records = 0}};
Теперь вы готовы начать чтение из вашего файла, и insert_data
для правильного состояния, основанного на сокращении, считанном из файла.Простой способ приблизиться к чтению - это прочитать "TN"
в отдельный массив, а затем прочитать данные о климате во временную структуру типа climate_t
, которую можно передать в функцию insert_data
,В вашей функции insert_data
вы просто просматриваете индекс (выделяете или перераспределяете для data
при необходимости), а затем назначаете временную структуру данных для блока памяти для вашего state.data.Например, ваша функция insert_data
может выглядеть примерно так:
/* insert data for state given code and climate_t containing data */
int insert_data (statedata_t *st, const char *code, climate_t *data)
{
int index = lookupabrv (code); /* lookup array index */
if (index == -1) /* handle error */
return 0;
if (!st[index].num_allocated) { /* allocate data if not allocated */
st[index].data = malloc (NDATA * sizeof *st[index].data);
if (!st[index].data) {
perror ("malloc-st[index].data");
return 0;
}
st[index].num_allocated = NDATA;
}
/* check if realloc needed */
if (st[index].num_records == st[index].num_allocated) {
/* realloc here, update num_allocated */
}
/* add data for proper state index */
st[index].data[st[index].num_records++] = *data;
return 1; /* return success */
}
Вот и все.То, как вы анализируете информацию из каждой строки, зависит от вас, но для целей моего примера, учитывая ваши данные для примера, я просто использовал sscanf
для простоты.В целом, вы можете сделать что-то вроде следующего:
#include <stdio.h>
#include <stdlib.h>
/* if you need constants, either #define them or use an enum */
enum { ABRV = 2, NDATA = 8, LOC = 13, NAME = 15, MAXC = 1024 };
typedef struct {
char name[NAME+1],
abrv[ABRV+1];
} stateabrv_t;
typedef struct { /* struct holding only climate data */
long long millitime;
char location[LOC];
double humidity;
double snow;
double cloud;
double lightning;
long double pressure;
double temperature;
} climate_t;
typedef struct {
size_t num_allocated, /* track of how many data are allocated */
num_records;
climate_t *data; /* a pointer to allocated block for data */
} statedata_t;
const stateabrv_t state[] = { { "Alabama", "AL" },
{ "Alaska", "AK" },
{ "Arizona", "AZ" },
{ "Arkansas", "AR" },
{ "California", "CA" },
{ "Colorado", "CO" },
{ "Connecticut", "CT" },
{ "Delaware", "DE" },
{ "Florida", "FL" },
{ "Georgia", "GA" },
{ "Hawaii", "HI" },
{ "Idaho", "ID" },
{ "Illinois", "IL" },
{ "Indiana", "IN" },
{ "Iowa", "IA" },
{ "Kansas", "KS" },
{ "Kentucky", "KY" },
{ "Louisiana", "LA" },
{ "Maine", "ME" },
{ "Maryland", "MD" },
{ "Massachusetts", "MA" },
{ "Michigan", "MI" },
{ "Minnesota", "MN" },
{ "Mississippi", "MS" },
{ "Missouri", "MO" },
{ "Montana", "MT" },
{ "Nebraska", "NE" },
{ "Nevada", "NV" },
{ "New Hampshire", "NH" },
{ "New Jersey", "NJ" },
{ "New Mexico", "NM" },
{ "New York", "NY" },
{ "North Carolina", "NC" },
{ "North Dakota", "ND" },
{ "Ohio", "OH" },
{ "Oklahoma", "OK" },
{ "Oregon", "OR" },
{ "Pennsylvania", "PA" },
{ "Rhode Island", "RI" },
{ "South Carolina", "SC" },
{ "South Dakota", "SD" },
{ "Tennessee", "TN" },
{ "Texas", "TX" },
{ "Utah", "UT" },
{ "Vermont", "VT" },
{ "Virginia", "VA" },
{ "Washington", "WA" },
{ "West Virginia", "WV" },
{ "Wisconsin", "WI" },
{ "Wyoming", "WY" } };
const int nstates = sizeof state / sizeof *state;
/* simple lookup function, given a code s, return index for state
* in array of statedata_t on success, -1 otherwise.
*/
int lookupabrv (const char *s)
{
int i = 0;
for (; i < nstates; i++)
if (state[i].abrv[0] == s[0] && state[i].abrv[1] == s[1])
return i;
return -1;
}
/* insert data for state given code and climate_t containing data */
int insert_data (statedata_t *st, const char *code, climate_t *data)
{
int index = lookupabrv (code); /* lookup array index */
if (index == -1) /* handle error */
return 0;
if (!st[index].num_allocated) { /* allocate data if not allocated */
st[index].data = malloc (NDATA * sizeof *st[index].data);
if (!st[index].data) {
perror ("malloc-st[index].data");
return 0;
}
st[index].num_allocated = NDATA;
}
/* check if realloc needed */
if (st[index].num_records == st[index].num_allocated) {
/* realloc here, update num_allocated */
}
/* add data for proper state index */
st[index].data[st[index].num_records++] = *data;
return 1; /* return success */
}
/* print states with data collected */
void print_data (statedata_t *st)
{
int i = 0;
for (; i < nstates; i++) {
if (st[i].num_records) {
size_t j = 0;
printf ("\n%s\n", state[i].name);
for (; j < st[i].num_records; j++)
printf (" %13lld %-12s %5.1f %5.1f %5.1f %5.1f %8.1Lf "
"%8.4f\n",
st[i].data[j].millitime, st[i].data[j].location,
st[i].data[j].humidity, st[i].data[j].snow,
st[i].data[j].cloud, st[i].data[j].lightning,
st[i].data[j].pressure, st[i].data[j].temperature);
}
}
}
/* free allocated memory */
void free_data (statedata_t *st)
{
int i = 0;
for (; i < nstates; i++)
if (st[i].num_records)
free (st[i].data);
}
int main (int argc, char **argv) {
char buf[MAXC]; /* line buffer */
/* array of 50 statedata_t (one for each state) */
statedata_t stdata[sizeof state / sizeof *state] = {{.num_records = 0}};
/* read from file given as argument (or stdin if none given) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line of data */
char code[ABRV+1] = ""; /* declare storage for abriviation */
climate_t tmp = { .millitime = 0 }; /* declare temp stuct for data */
/* simple parse of data with sscanf */
if (sscanf (buf, "%2s %lld %12s %lf %lf %lf %lf %Lf %lf", code,
&tmp.millitime, tmp.location, &tmp.humidity, &tmp.snow,
&tmp.cloud, &tmp.lightning, &tmp.pressure, &tmp.temperature)
== 9) {
if (!insert_data (stdata, code, &tmp)) /* insert data/validate */
fprintf (stderr, "error: insert_data failed (%s).\n", code);
}
else /* handle error */
fprintf (stderr, "error: invalid format:\n%s\n", buf);
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
print_data (stdata); /* print data */
free_data (stdata); /* free allocated memory */
return 0;
}
Пример входного файла
$ cat dat/state_climate.txt
TN 1424325600000 dn20t1kz0xrz 67.0 0.0 0.0 0.0 101872.0 262.5665
TN 1422770400000 dn2dcstxsf5b 23.0 0.0 100.0 0.0 100576.0 277.8087
TN 1422792000000 dn2sdp6pbb5b 96.0 0.0 100.0 0.0 100117.0 278.49207
TN 1422748800000 dn2fjteh8e80 6.0 0.0 100.0 0.0 100661.0 278.28485
TN 1423396800000 dn2k0y7ffcup 14.0 0.0 100.0 0.0 100176.0 282.02142
Пример использования / Вывод
$ ./bin/state_climate <dat/state_climate.txt
Tennessee
1424325600000 dn20t1kz0xrz 67.0 0.0 0.0 0.0 101872.0 262.5665
1422770400000 dn2dcstxsf5b 23.0 0.0 100.0 0.0 100576.0 277.8087
1422792000000 dn2sdp6pbb5b 96.0 0.0 100.0 0.0 100117.0 278.4921
1422748800000 dn2fjteh8e80 6.0 0.0 100.0 0.0 100661.0 278.2849
1423396800000 dn2k0y7ffcup 14.0 0.0 100.0 0.0 100176.0 282.0214
Использование памяти / проверка ошибок
В любом написанном вами коде, который динамически распределяет память, у вас есть 2 обязанностей относительно любого блока памятираспределены: (1) всегда сохранять указатель на начальный адрес для блока памяти, поэтому (2) он может быть освобожден , когда он больше не нужен.
Крайне важно, чтобы вы использовали программу проверки ошибок памяти, чтобы убедиться, что вы не пытаетесь получить доступ к памяти или писать за пределами / за пределами выделенного блока, пытаться прочитать или основать условный переход на неинициализированном значении и, наконец,чтобы подтвердить, что вы освобождаете всю выделенную память.
Для Linux valgrind
- нормальный выбор.Для каждой платформы есть похожие проверки памяти.Все они просты в использовании, просто запустите вашу программу через него.
$ valgrind ./bin/state_climate <dat/state_climate.txt
==6157== Memcheck, a memory error detector
==6157== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==6157== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==6157== Command: ./bin/state_climate
==6157==
Tennessee
1424325600000 dn20t1kz0xrz 67.0 0.0 0.0 0.0 101872.0 262.5665
1422770400000 dn2dcstxsf5b 23.0 0.0 100.0 0.0 100576.0 277.8087
1422792000000 dn2sdp6pbb5b 96.0 0.0 100.0 0.0 100117.0 278.4921
1422748800000 dn2fjteh8e80 6.0 0.0 100.0 0.0 100661.0 278.2849
1423396800000 dn2k0y7ffcup 14.0 0.0 100.0 0.0 100176.0 282.0214
==6157==
==6157== HEAP SUMMARY:
==6157== in use at exit: 0 bytes in 0 blocks
==6157== total heap usage: 1 allocs, 1 frees, 768 bytes allocated
==6157==
==6157== All heap blocks were freed -- no leaks are possible
==6157==
==6157== For counts of detected and suppressed errors, rerun with: -v
==6157== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.
Посмотрите вещии подумайте, почему изменение в структурах имеет смысл.Дайте мне знать, если у вас есть какие-либо вопросы.