Tokening An External File
So I have been stuck as to how to token the FIRST token and place that value into a struct. In my case I am trying to read the lines from a file that looks like this:
TDV format:
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
As you can see there is a TN which indicated a state's code. In my function below I need to be able to recognize that a line is for a particular state and send that to a struct.
Here is a the function where I am supposed to do this. I have commented the list of things I need to do in this function. I thought I was doing it right however when I print it out it turns out something totally different is actually happening:
void analyze_file(FILE *file, struct climate_info **states, int num_states)
{
const int line_sz = 100;
char line[line_sz];
int counter = 0;
char *token;
while (fgets(line, line_sz, file) != NULL)
{
/* TODO: We need to do a few things here:
*
* * Tokenize the line.
* * Determine what state the line is for. This will be the state
* code, stored as our first token.
* * If our states array doesn't have a climate_info entry for
* this state, then we need to allocate memory for it and put it
* in the next open place in the array. Otherwise, we reuse the
* existing entry.
* * Update the climate_info structure as necessary.
*/
struct climate_info *states = malloc(sizeof(struct climate_info)*num_states);
token = strtok(line," n");
strcpy(states->code, token);
//printf("token: %sn", token);
while(token)
{
printf("token: %sn", token);
token = strtok(NULL, " t");
}
}
printf("%dn",counter);
}
Here is my struct that I have defined:
struct climate_info
{
char code[3];
unsigned long num_records;
long long millitime;
char location[13];
double humidity;
double snow;
double cloud;
double lightning;
long double pressure;
double temperature;
};
Here is where I print my output and this is where my program doesn't seem to recognize what is being done in the analyze_file function:
void print_report(struct climate_info *states, int num_states)
{
printf("States found: ");
int i;
for (i = 0; i < num_states; ++i)
{
if (states[i] != NULL)
{
struct climate_info *info = states[i];
printf("%s", info->code);
}
}
printf("n");
The output should look like this: States found: TN
I am able to tokening my string and output each an every token of every line however the problem is when I try to give the structure values. In my line in analyze_file: strcpy(states->code, token); I am trying to take the first token which I know is the state code and give it to the allocated space I have created from my structure. As you can see from my print_report function it doesn't seem to recognize that I am sending values to the climate info. My question is how do I fix my analyze_file function without changing the print_report function.
c struct dynamic-memory-allocation strtok strcpy
|
show 6 more comments
So I have been stuck as to how to token the FIRST token and place that value into a struct. In my case I am trying to read the lines from a file that looks like this:
TDV format:
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
As you can see there is a TN which indicated a state's code. In my function below I need to be able to recognize that a line is for a particular state and send that to a struct.
Here is a the function where I am supposed to do this. I have commented the list of things I need to do in this function. I thought I was doing it right however when I print it out it turns out something totally different is actually happening:
void analyze_file(FILE *file, struct climate_info **states, int num_states)
{
const int line_sz = 100;
char line[line_sz];
int counter = 0;
char *token;
while (fgets(line, line_sz, file) != NULL)
{
/* TODO: We need to do a few things here:
*
* * Tokenize the line.
* * Determine what state the line is for. This will be the state
* code, stored as our first token.
* * If our states array doesn't have a climate_info entry for
* this state, then we need to allocate memory for it and put it
* in the next open place in the array. Otherwise, we reuse the
* existing entry.
* * Update the climate_info structure as necessary.
*/
struct climate_info *states = malloc(sizeof(struct climate_info)*num_states);
token = strtok(line," n");
strcpy(states->code, token);
//printf("token: %sn", token);
while(token)
{
printf("token: %sn", token);
token = strtok(NULL, " t");
}
}
printf("%dn",counter);
}
Here is my struct that I have defined:
struct climate_info
{
char code[3];
unsigned long num_records;
long long millitime;
char location[13];
double humidity;
double snow;
double cloud;
double lightning;
long double pressure;
double temperature;
};
Here is where I print my output and this is where my program doesn't seem to recognize what is being done in the analyze_file function:
void print_report(struct climate_info *states, int num_states)
{
printf("States found: ");
int i;
for (i = 0; i < num_states; ++i)
{
if (states[i] != NULL)
{
struct climate_info *info = states[i];
printf("%s", info->code);
}
}
printf("n");
The output should look like this: States found: TN
I am able to tokening my string and output each an every token of every line however the problem is when I try to give the structure values. In my line in analyze_file: strcpy(states->code, token); I am trying to take the first token which I know is the state code and give it to the allocated space I have created from my structure. As you can see from my print_report function it doesn't seem to recognize that I am sending values to the climate info. My question is how do I fix my analyze_file function without changing the print_report function.
c struct dynamic-memory-allocation strtok strcpy
This is at least closely related to How to assign values dynamically to a struct? . I've not yet worked out what's different from what was discussed there yesterday.
– Jonathan Leffler
Nov 24 '18 at 2:37
What is your output ? Could it be that your file has fields separated byt
, that is not in your separators string ?
– alamit
Nov 24 '18 at 2:43
1
This looks like a good job for simple look-up function that given"TN"
returns an index where the information for Tennessee is stored instates
. Then it's a simple matter of reading"TN"
looking up the index, and writing the remaining data there.
– David C. Rankin
Nov 24 '18 at 2:50
@alamit Look again.
– Swordfish
Nov 24 '18 at 3:18
1
It really doesn't make a whole lot of difference, but if you are dealing with climate data for the US states, then I would think you would want an array of 50struct state
where each state has a pointer tostruct climate_info
. That way you coordinate all data by state with the data for each state within. If you were designing a database, then the records approach you are taking would make more sense since the database is designed to run "queries" against the information. But from a program standpoint it may make more sense to collect data by state. Worth considering.
– David C. Rankin
Nov 24 '18 at 4:18
|
show 6 more comments
So I have been stuck as to how to token the FIRST token and place that value into a struct. In my case I am trying to read the lines from a file that looks like this:
TDV format:
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
As you can see there is a TN which indicated a state's code. In my function below I need to be able to recognize that a line is for a particular state and send that to a struct.
Here is a the function where I am supposed to do this. I have commented the list of things I need to do in this function. I thought I was doing it right however when I print it out it turns out something totally different is actually happening:
void analyze_file(FILE *file, struct climate_info **states, int num_states)
{
const int line_sz = 100;
char line[line_sz];
int counter = 0;
char *token;
while (fgets(line, line_sz, file) != NULL)
{
/* TODO: We need to do a few things here:
*
* * Tokenize the line.
* * Determine what state the line is for. This will be the state
* code, stored as our first token.
* * If our states array doesn't have a climate_info entry for
* this state, then we need to allocate memory for it and put it
* in the next open place in the array. Otherwise, we reuse the
* existing entry.
* * Update the climate_info structure as necessary.
*/
struct climate_info *states = malloc(sizeof(struct climate_info)*num_states);
token = strtok(line," n");
strcpy(states->code, token);
//printf("token: %sn", token);
while(token)
{
printf("token: %sn", token);
token = strtok(NULL, " t");
}
}
printf("%dn",counter);
}
Here is my struct that I have defined:
struct climate_info
{
char code[3];
unsigned long num_records;
long long millitime;
char location[13];
double humidity;
double snow;
double cloud;
double lightning;
long double pressure;
double temperature;
};
Here is where I print my output and this is where my program doesn't seem to recognize what is being done in the analyze_file function:
void print_report(struct climate_info *states, int num_states)
{
printf("States found: ");
int i;
for (i = 0; i < num_states; ++i)
{
if (states[i] != NULL)
{
struct climate_info *info = states[i];
printf("%s", info->code);
}
}
printf("n");
The output should look like this: States found: TN
I am able to tokening my string and output each an every token of every line however the problem is when I try to give the structure values. In my line in analyze_file: strcpy(states->code, token); I am trying to take the first token which I know is the state code and give it to the allocated space I have created from my structure. As you can see from my print_report function it doesn't seem to recognize that I am sending values to the climate info. My question is how do I fix my analyze_file function without changing the print_report function.
c struct dynamic-memory-allocation strtok strcpy
So I have been stuck as to how to token the FIRST token and place that value into a struct. In my case I am trying to read the lines from a file that looks like this:
TDV format:
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
As you can see there is a TN which indicated a state's code. In my function below I need to be able to recognize that a line is for a particular state and send that to a struct.
Here is a the function where I am supposed to do this. I have commented the list of things I need to do in this function. I thought I was doing it right however when I print it out it turns out something totally different is actually happening:
void analyze_file(FILE *file, struct climate_info **states, int num_states)
{
const int line_sz = 100;
char line[line_sz];
int counter = 0;
char *token;
while (fgets(line, line_sz, file) != NULL)
{
/* TODO: We need to do a few things here:
*
* * Tokenize the line.
* * Determine what state the line is for. This will be the state
* code, stored as our first token.
* * If our states array doesn't have a climate_info entry for
* this state, then we need to allocate memory for it and put it
* in the next open place in the array. Otherwise, we reuse the
* existing entry.
* * Update the climate_info structure as necessary.
*/
struct climate_info *states = malloc(sizeof(struct climate_info)*num_states);
token = strtok(line," n");
strcpy(states->code, token);
//printf("token: %sn", token);
while(token)
{
printf("token: %sn", token);
token = strtok(NULL, " t");
}
}
printf("%dn",counter);
}
Here is my struct that I have defined:
struct climate_info
{
char code[3];
unsigned long num_records;
long long millitime;
char location[13];
double humidity;
double snow;
double cloud;
double lightning;
long double pressure;
double temperature;
};
Here is where I print my output and this is where my program doesn't seem to recognize what is being done in the analyze_file function:
void print_report(struct climate_info *states, int num_states)
{
printf("States found: ");
int i;
for (i = 0; i < num_states; ++i)
{
if (states[i] != NULL)
{
struct climate_info *info = states[i];
printf("%s", info->code);
}
}
printf("n");
The output should look like this: States found: TN
I am able to tokening my string and output each an every token of every line however the problem is when I try to give the structure values. In my line in analyze_file: strcpy(states->code, token); I am trying to take the first token which I know is the state code and give it to the allocated space I have created from my structure. As you can see from my print_report function it doesn't seem to recognize that I am sending values to the climate info. My question is how do I fix my analyze_file function without changing the print_report function.
c struct dynamic-memory-allocation strtok strcpy
c struct dynamic-memory-allocation strtok strcpy
asked Nov 24 '18 at 2:32
JustAJAVAGUYJustAJAVAGUY
397
397
This is at least closely related to How to assign values dynamically to a struct? . I've not yet worked out what's different from what was discussed there yesterday.
– Jonathan Leffler
Nov 24 '18 at 2:37
What is your output ? Could it be that your file has fields separated byt
, that is not in your separators string ?
– alamit
Nov 24 '18 at 2:43
1
This looks like a good job for simple look-up function that given"TN"
returns an index where the information for Tennessee is stored instates
. Then it's a simple matter of reading"TN"
looking up the index, and writing the remaining data there.
– David C. Rankin
Nov 24 '18 at 2:50
@alamit Look again.
– Swordfish
Nov 24 '18 at 3:18
1
It really doesn't make a whole lot of difference, but if you are dealing with climate data for the US states, then I would think you would want an array of 50struct state
where each state has a pointer tostruct climate_info
. That way you coordinate all data by state with the data for each state within. If you were designing a database, then the records approach you are taking would make more sense since the database is designed to run "queries" against the information. But from a program standpoint it may make more sense to collect data by state. Worth considering.
– David C. Rankin
Nov 24 '18 at 4:18
|
show 6 more comments
This is at least closely related to How to assign values dynamically to a struct? . I've not yet worked out what's different from what was discussed there yesterday.
– Jonathan Leffler
Nov 24 '18 at 2:37
What is your output ? Could it be that your file has fields separated byt
, that is not in your separators string ?
– alamit
Nov 24 '18 at 2:43
1
This looks like a good job for simple look-up function that given"TN"
returns an index where the information for Tennessee is stored instates
. Then it's a simple matter of reading"TN"
looking up the index, and writing the remaining data there.
– David C. Rankin
Nov 24 '18 at 2:50
@alamit Look again.
– Swordfish
Nov 24 '18 at 3:18
1
It really doesn't make a whole lot of difference, but if you are dealing with climate data for the US states, then I would think you would want an array of 50struct state
where each state has a pointer tostruct climate_info
. That way you coordinate all data by state with the data for each state within. If you were designing a database, then the records approach you are taking would make more sense since the database is designed to run "queries" against the information. But from a program standpoint it may make more sense to collect data by state. Worth considering.
– David C. Rankin
Nov 24 '18 at 4:18
This is at least closely related to How to assign values dynamically to a struct? . I've not yet worked out what's different from what was discussed there yesterday.
– Jonathan Leffler
Nov 24 '18 at 2:37
This is at least closely related to How to assign values dynamically to a struct? . I've not yet worked out what's different from what was discussed there yesterday.
– Jonathan Leffler
Nov 24 '18 at 2:37
What is your output ? Could it be that your file has fields separated by
t
, that is not in your separators string ?– alamit
Nov 24 '18 at 2:43
What is your output ? Could it be that your file has fields separated by
t
, that is not in your separators string ?– alamit
Nov 24 '18 at 2:43
1
1
This looks like a good job for simple look-up function that given
"TN"
returns an index where the information for Tennessee is stored in states
. Then it's a simple matter of reading "TN"
looking up the index, and writing the remaining data there.– David C. Rankin
Nov 24 '18 at 2:50
This looks like a good job for simple look-up function that given
"TN"
returns an index where the information for Tennessee is stored in states
. Then it's a simple matter of reading "TN"
looking up the index, and writing the remaining data there.– David C. Rankin
Nov 24 '18 at 2:50
@alamit Look again.
– Swordfish
Nov 24 '18 at 3:18
@alamit Look again.
– Swordfish
Nov 24 '18 at 3:18
1
1
It really doesn't make a whole lot of difference, but if you are dealing with climate data for the US states, then I would think you would want an array of 50
struct state
where each state has a pointer to struct climate_info
. That way you coordinate all data by state with the data for each state within. If you were designing a database, then the records approach you are taking would make more sense since the database is designed to run "queries" against the information. But from a program standpoint it may make more sense to collect data by state. Worth considering.– David C. Rankin
Nov 24 '18 at 4:18
It really doesn't make a whole lot of difference, but if you are dealing with climate data for the US states, then I would think you would want an array of 50
struct state
where each state has a pointer to struct climate_info
. That way you coordinate all data by state with the data for each state within. If you were designing a database, then the records approach you are taking would make more sense since the database is designed to run "queries" against the information. But from a program standpoint it may make more sense to collect data by state. Worth considering.– David C. Rankin
Nov 24 '18 at 4:18
|
show 6 more comments
1 Answer
1
active
oldest
votes
It appears much of the difficulty you are having in trying to figure out how to use the "TN"
stems from your attempt to store all data read in each line in a separate struct. As mentioned in the comments, this may be fine for reading data into a database where the database provides the ability to query all records by state abbreviation, but makes handling the data a bit more awkward. Why?
When you store all records as individual struct, there is no relationship between the state the data belongs to and the information stored other than a code
member of the struct. This means that if you wish to search or print the information for, e.g. "TN"
you must traverse over every single stuct checking whether the code
member matches "TN"
. Think about printing. You have to loop for each state, and then loop over every struct each time picking out information for a single state to print.
Instead of storing every record of information as an element in an array of records, why not have an array of states where each state contains a pointer to the data for that state. That would make your num_records
member make more sense. You would then simply have to loop over your array of states, check whether (num_records > 0)
and then printing the num_records
worth of information for that state while skipping all states where no data has been stored. This provides a much more efficient approach.
For example it takes little effort to rearrange your structs slightly to provide a relationship between the state and the data associated with that state, e.g.:
#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;
But how to relate reading "TN"
from the file to get the data stored with the correct state? This is where the lookup table comes in. If you had another simple struct containing the state name and abbreviation, you could create a simple array of struct that holds the abbreviation information and when you read, e.g. "TN"
from the file, you could simply "look-up" the index where "TN"
lives in your array holding abbreviations, and then use that index to store the information from that line at the corresponding index in your statedata_t
array.
Since your "lookup-array" will be constant, it can simply be a global that is declared as const
. If you are using multiple source files, you can simply define the array in one file and declare it a extern
in the remaining files that need it. So how would you define it? First declare a sturct with the information you want in your lookup (the state name and abbreviation) and then declare a constant array of them initializing the name and abbreviation for each, e.g.
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;
Now you have a simple 2-way lookup. Given the state name or abbreviation, you can return the index where it lives in the array. Further, given the name you can lookup the abbreviation, or given the abbreviation, you can lookup the name.
A simple lookup function returning the index could be:
/* 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;
}
Now that you can find the index given the abbreviation using your global lookup table, you can put the remainder of your data handling together in main()
by declaring an array of 50 statedata_t
, e.g.
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}};
Now you are ready to begin reading from your file, and insert_data
for the proper state based on the abbreviation read from the file. A simple way to approach the read is to read the "TN"
into a separate array, and then read the climate data into a temporary stuct of type climate_t
that you can pass to your insert_data
function. In your insert_data
function, you simply lookup the index, (allocate or reallocate for data
as needed) and then assign your temporary struct of data to the memory block for your state.data. For example, your insert_data
function could be something like the following:
/* 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 */
}
That's basically it. How you parse the information from each line is up to you, but for purposes of my example, given your sample data, I simply used sscanf
for simplicity. Putting it altogether, you could do something like the following:
#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%sn", state[i].name);
for (; j < st[i].num_records; j++)
printf (" %13lld %-12s %5.1f %5.1f %5.1f %5.1f %8.1Lf "
"%8.4fn",
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%sn", buf);
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
print_data (stdata); /* print data */
free_data (stdata); /* free allocated memory */
return 0;
}
Example Input File
$ 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
Example Use/Output
$ ./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
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ 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)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and consider why the change in the structs makes sense. Let me know if you have any questions.
Wow Thank you So much for this extremely detailed and thorough answer. I was very confused about structs and this was very helpful!
– JustAJAVAGUY
Nov 24 '18 at 10:02
1
Sure, glad to help. Sometimes it just takes seeing a lookup-table to have it make sense. There are a lot of different applications for a simple lookup, generally to coordinate indexes between two different (but related) strutcs, etc.. Good luck with your coding.
– David C. Rankin
Nov 24 '18 at 10:33
Hi David, I wanted to ask you something, can you please start look at the message I've sent you on chat please
– JustAJAVAGUY
Nov 24 '18 at 10:51
1
drankinatty at gmail will do. - I'll be in and out today, I have some household chores to get to.... (oh joy....)
– David C. Rankin
Nov 24 '18 at 16:35
1
I have sent you a response -- got the trailer rebuilt, so now the tractor won't fall through the deck -- so now... more chores...
– David C. Rankin
Nov 25 '18 at 4:14
|
show 2 more comments
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53454699%2ftokening-an-external-file%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
It appears much of the difficulty you are having in trying to figure out how to use the "TN"
stems from your attempt to store all data read in each line in a separate struct. As mentioned in the comments, this may be fine for reading data into a database where the database provides the ability to query all records by state abbreviation, but makes handling the data a bit more awkward. Why?
When you store all records as individual struct, there is no relationship between the state the data belongs to and the information stored other than a code
member of the struct. This means that if you wish to search or print the information for, e.g. "TN"
you must traverse over every single stuct checking whether the code
member matches "TN"
. Think about printing. You have to loop for each state, and then loop over every struct each time picking out information for a single state to print.
Instead of storing every record of information as an element in an array of records, why not have an array of states where each state contains a pointer to the data for that state. That would make your num_records
member make more sense. You would then simply have to loop over your array of states, check whether (num_records > 0)
and then printing the num_records
worth of information for that state while skipping all states where no data has been stored. This provides a much more efficient approach.
For example it takes little effort to rearrange your structs slightly to provide a relationship between the state and the data associated with that state, e.g.:
#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;
But how to relate reading "TN"
from the file to get the data stored with the correct state? This is where the lookup table comes in. If you had another simple struct containing the state name and abbreviation, you could create a simple array of struct that holds the abbreviation information and when you read, e.g. "TN"
from the file, you could simply "look-up" the index where "TN"
lives in your array holding abbreviations, and then use that index to store the information from that line at the corresponding index in your statedata_t
array.
Since your "lookup-array" will be constant, it can simply be a global that is declared as const
. If you are using multiple source files, you can simply define the array in one file and declare it a extern
in the remaining files that need it. So how would you define it? First declare a sturct with the information you want in your lookup (the state name and abbreviation) and then declare a constant array of them initializing the name and abbreviation for each, e.g.
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;
Now you have a simple 2-way lookup. Given the state name or abbreviation, you can return the index where it lives in the array. Further, given the name you can lookup the abbreviation, or given the abbreviation, you can lookup the name.
A simple lookup function returning the index could be:
/* 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;
}
Now that you can find the index given the abbreviation using your global lookup table, you can put the remainder of your data handling together in main()
by declaring an array of 50 statedata_t
, e.g.
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}};
Now you are ready to begin reading from your file, and insert_data
for the proper state based on the abbreviation read from the file. A simple way to approach the read is to read the "TN"
into a separate array, and then read the climate data into a temporary stuct of type climate_t
that you can pass to your insert_data
function. In your insert_data
function, you simply lookup the index, (allocate or reallocate for data
as needed) and then assign your temporary struct of data to the memory block for your state.data. For example, your insert_data
function could be something like the following:
/* 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 */
}
That's basically it. How you parse the information from each line is up to you, but for purposes of my example, given your sample data, I simply used sscanf
for simplicity. Putting it altogether, you could do something like the following:
#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%sn", state[i].name);
for (; j < st[i].num_records; j++)
printf (" %13lld %-12s %5.1f %5.1f %5.1f %5.1f %8.1Lf "
"%8.4fn",
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%sn", buf);
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
print_data (stdata); /* print data */
free_data (stdata); /* free allocated memory */
return 0;
}
Example Input File
$ 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
Example Use/Output
$ ./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
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ 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)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and consider why the change in the structs makes sense. Let me know if you have any questions.
Wow Thank you So much for this extremely detailed and thorough answer. I was very confused about structs and this was very helpful!
– JustAJAVAGUY
Nov 24 '18 at 10:02
1
Sure, glad to help. Sometimes it just takes seeing a lookup-table to have it make sense. There are a lot of different applications for a simple lookup, generally to coordinate indexes between two different (but related) strutcs, etc.. Good luck with your coding.
– David C. Rankin
Nov 24 '18 at 10:33
Hi David, I wanted to ask you something, can you please start look at the message I've sent you on chat please
– JustAJAVAGUY
Nov 24 '18 at 10:51
1
drankinatty at gmail will do. - I'll be in and out today, I have some household chores to get to.... (oh joy....)
– David C. Rankin
Nov 24 '18 at 16:35
1
I have sent you a response -- got the trailer rebuilt, so now the tractor won't fall through the deck -- so now... more chores...
– David C. Rankin
Nov 25 '18 at 4:14
|
show 2 more comments
It appears much of the difficulty you are having in trying to figure out how to use the "TN"
stems from your attempt to store all data read in each line in a separate struct. As mentioned in the comments, this may be fine for reading data into a database where the database provides the ability to query all records by state abbreviation, but makes handling the data a bit more awkward. Why?
When you store all records as individual struct, there is no relationship between the state the data belongs to and the information stored other than a code
member of the struct. This means that if you wish to search or print the information for, e.g. "TN"
you must traverse over every single stuct checking whether the code
member matches "TN"
. Think about printing. You have to loop for each state, and then loop over every struct each time picking out information for a single state to print.
Instead of storing every record of information as an element in an array of records, why not have an array of states where each state contains a pointer to the data for that state. That would make your num_records
member make more sense. You would then simply have to loop over your array of states, check whether (num_records > 0)
and then printing the num_records
worth of information for that state while skipping all states where no data has been stored. This provides a much more efficient approach.
For example it takes little effort to rearrange your structs slightly to provide a relationship between the state and the data associated with that state, e.g.:
#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;
But how to relate reading "TN"
from the file to get the data stored with the correct state? This is where the lookup table comes in. If you had another simple struct containing the state name and abbreviation, you could create a simple array of struct that holds the abbreviation information and when you read, e.g. "TN"
from the file, you could simply "look-up" the index where "TN"
lives in your array holding abbreviations, and then use that index to store the information from that line at the corresponding index in your statedata_t
array.
Since your "lookup-array" will be constant, it can simply be a global that is declared as const
. If you are using multiple source files, you can simply define the array in one file and declare it a extern
in the remaining files that need it. So how would you define it? First declare a sturct with the information you want in your lookup (the state name and abbreviation) and then declare a constant array of them initializing the name and abbreviation for each, e.g.
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;
Now you have a simple 2-way lookup. Given the state name or abbreviation, you can return the index where it lives in the array. Further, given the name you can lookup the abbreviation, or given the abbreviation, you can lookup the name.
A simple lookup function returning the index could be:
/* 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;
}
Now that you can find the index given the abbreviation using your global lookup table, you can put the remainder of your data handling together in main()
by declaring an array of 50 statedata_t
, e.g.
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}};
Now you are ready to begin reading from your file, and insert_data
for the proper state based on the abbreviation read from the file. A simple way to approach the read is to read the "TN"
into a separate array, and then read the climate data into a temporary stuct of type climate_t
that you can pass to your insert_data
function. In your insert_data
function, you simply lookup the index, (allocate or reallocate for data
as needed) and then assign your temporary struct of data to the memory block for your state.data. For example, your insert_data
function could be something like the following:
/* 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 */
}
That's basically it. How you parse the information from each line is up to you, but for purposes of my example, given your sample data, I simply used sscanf
for simplicity. Putting it altogether, you could do something like the following:
#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%sn", state[i].name);
for (; j < st[i].num_records; j++)
printf (" %13lld %-12s %5.1f %5.1f %5.1f %5.1f %8.1Lf "
"%8.4fn",
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%sn", buf);
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
print_data (stdata); /* print data */
free_data (stdata); /* free allocated memory */
return 0;
}
Example Input File
$ 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
Example Use/Output
$ ./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
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ 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)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and consider why the change in the structs makes sense. Let me know if you have any questions.
Wow Thank you So much for this extremely detailed and thorough answer. I was very confused about structs and this was very helpful!
– JustAJAVAGUY
Nov 24 '18 at 10:02
1
Sure, glad to help. Sometimes it just takes seeing a lookup-table to have it make sense. There are a lot of different applications for a simple lookup, generally to coordinate indexes between two different (but related) strutcs, etc.. Good luck with your coding.
– David C. Rankin
Nov 24 '18 at 10:33
Hi David, I wanted to ask you something, can you please start look at the message I've sent you on chat please
– JustAJAVAGUY
Nov 24 '18 at 10:51
1
drankinatty at gmail will do. - I'll be in and out today, I have some household chores to get to.... (oh joy....)
– David C. Rankin
Nov 24 '18 at 16:35
1
I have sent you a response -- got the trailer rebuilt, so now the tractor won't fall through the deck -- so now... more chores...
– David C. Rankin
Nov 25 '18 at 4:14
|
show 2 more comments
It appears much of the difficulty you are having in trying to figure out how to use the "TN"
stems from your attempt to store all data read in each line in a separate struct. As mentioned in the comments, this may be fine for reading data into a database where the database provides the ability to query all records by state abbreviation, but makes handling the data a bit more awkward. Why?
When you store all records as individual struct, there is no relationship between the state the data belongs to and the information stored other than a code
member of the struct. This means that if you wish to search or print the information for, e.g. "TN"
you must traverse over every single stuct checking whether the code
member matches "TN"
. Think about printing. You have to loop for each state, and then loop over every struct each time picking out information for a single state to print.
Instead of storing every record of information as an element in an array of records, why not have an array of states where each state contains a pointer to the data for that state. That would make your num_records
member make more sense. You would then simply have to loop over your array of states, check whether (num_records > 0)
and then printing the num_records
worth of information for that state while skipping all states where no data has been stored. This provides a much more efficient approach.
For example it takes little effort to rearrange your structs slightly to provide a relationship between the state and the data associated with that state, e.g.:
#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;
But how to relate reading "TN"
from the file to get the data stored with the correct state? This is where the lookup table comes in. If you had another simple struct containing the state name and abbreviation, you could create a simple array of struct that holds the abbreviation information and when you read, e.g. "TN"
from the file, you could simply "look-up" the index where "TN"
lives in your array holding abbreviations, and then use that index to store the information from that line at the corresponding index in your statedata_t
array.
Since your "lookup-array" will be constant, it can simply be a global that is declared as const
. If you are using multiple source files, you can simply define the array in one file and declare it a extern
in the remaining files that need it. So how would you define it? First declare a sturct with the information you want in your lookup (the state name and abbreviation) and then declare a constant array of them initializing the name and abbreviation for each, e.g.
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;
Now you have a simple 2-way lookup. Given the state name or abbreviation, you can return the index where it lives in the array. Further, given the name you can lookup the abbreviation, or given the abbreviation, you can lookup the name.
A simple lookup function returning the index could be:
/* 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;
}
Now that you can find the index given the abbreviation using your global lookup table, you can put the remainder of your data handling together in main()
by declaring an array of 50 statedata_t
, e.g.
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}};
Now you are ready to begin reading from your file, and insert_data
for the proper state based on the abbreviation read from the file. A simple way to approach the read is to read the "TN"
into a separate array, and then read the climate data into a temporary stuct of type climate_t
that you can pass to your insert_data
function. In your insert_data
function, you simply lookup the index, (allocate or reallocate for data
as needed) and then assign your temporary struct of data to the memory block for your state.data. For example, your insert_data
function could be something like the following:
/* 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 */
}
That's basically it. How you parse the information from each line is up to you, but for purposes of my example, given your sample data, I simply used sscanf
for simplicity. Putting it altogether, you could do something like the following:
#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%sn", state[i].name);
for (; j < st[i].num_records; j++)
printf (" %13lld %-12s %5.1f %5.1f %5.1f %5.1f %8.1Lf "
"%8.4fn",
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%sn", buf);
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
print_data (stdata); /* print data */
free_data (stdata); /* free allocated memory */
return 0;
}
Example Input File
$ 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
Example Use/Output
$ ./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
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ 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)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and consider why the change in the structs makes sense. Let me know if you have any questions.
It appears much of the difficulty you are having in trying to figure out how to use the "TN"
stems from your attempt to store all data read in each line in a separate struct. As mentioned in the comments, this may be fine for reading data into a database where the database provides the ability to query all records by state abbreviation, but makes handling the data a bit more awkward. Why?
When you store all records as individual struct, there is no relationship between the state the data belongs to and the information stored other than a code
member of the struct. This means that if you wish to search or print the information for, e.g. "TN"
you must traverse over every single stuct checking whether the code
member matches "TN"
. Think about printing. You have to loop for each state, and then loop over every struct each time picking out information for a single state to print.
Instead of storing every record of information as an element in an array of records, why not have an array of states where each state contains a pointer to the data for that state. That would make your num_records
member make more sense. You would then simply have to loop over your array of states, check whether (num_records > 0)
and then printing the num_records
worth of information for that state while skipping all states where no data has been stored. This provides a much more efficient approach.
For example it takes little effort to rearrange your structs slightly to provide a relationship between the state and the data associated with that state, e.g.:
#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;
But how to relate reading "TN"
from the file to get the data stored with the correct state? This is where the lookup table comes in. If you had another simple struct containing the state name and abbreviation, you could create a simple array of struct that holds the abbreviation information and when you read, e.g. "TN"
from the file, you could simply "look-up" the index where "TN"
lives in your array holding abbreviations, and then use that index to store the information from that line at the corresponding index in your statedata_t
array.
Since your "lookup-array" will be constant, it can simply be a global that is declared as const
. If you are using multiple source files, you can simply define the array in one file and declare it a extern
in the remaining files that need it. So how would you define it? First declare a sturct with the information you want in your lookup (the state name and abbreviation) and then declare a constant array of them initializing the name and abbreviation for each, e.g.
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;
Now you have a simple 2-way lookup. Given the state name or abbreviation, you can return the index where it lives in the array. Further, given the name you can lookup the abbreviation, or given the abbreviation, you can lookup the name.
A simple lookup function returning the index could be:
/* 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;
}
Now that you can find the index given the abbreviation using your global lookup table, you can put the remainder of your data handling together in main()
by declaring an array of 50 statedata_t
, e.g.
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}};
Now you are ready to begin reading from your file, and insert_data
for the proper state based on the abbreviation read from the file. A simple way to approach the read is to read the "TN"
into a separate array, and then read the climate data into a temporary stuct of type climate_t
that you can pass to your insert_data
function. In your insert_data
function, you simply lookup the index, (allocate or reallocate for data
as needed) and then assign your temporary struct of data to the memory block for your state.data. For example, your insert_data
function could be something like the following:
/* 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 */
}
That's basically it. How you parse the information from each line is up to you, but for purposes of my example, given your sample data, I simply used sscanf
for simplicity. Putting it altogether, you could do something like the following:
#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%sn", state[i].name);
for (; j < st[i].num_records; j++)
printf (" %13lld %-12s %5.1f %5.1f %5.1f %5.1f %8.1Lf "
"%8.4fn",
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%sn", buf);
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
print_data (stdata); /* print data */
free_data (stdata); /* free allocated memory */
return 0;
}
Example Input File
$ 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
Example Use/Output
$ ./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
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ 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)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and consider why the change in the structs makes sense. Let me know if you have any questions.
edited Nov 24 '18 at 7:12
answered Nov 24 '18 at 7:00
David C. RankinDavid C. Rankin
41k32648
41k32648
Wow Thank you So much for this extremely detailed and thorough answer. I was very confused about structs and this was very helpful!
– JustAJAVAGUY
Nov 24 '18 at 10:02
1
Sure, glad to help. Sometimes it just takes seeing a lookup-table to have it make sense. There are a lot of different applications for a simple lookup, generally to coordinate indexes between two different (but related) strutcs, etc.. Good luck with your coding.
– David C. Rankin
Nov 24 '18 at 10:33
Hi David, I wanted to ask you something, can you please start look at the message I've sent you on chat please
– JustAJAVAGUY
Nov 24 '18 at 10:51
1
drankinatty at gmail will do. - I'll be in and out today, I have some household chores to get to.... (oh joy....)
– David C. Rankin
Nov 24 '18 at 16:35
1
I have sent you a response -- got the trailer rebuilt, so now the tractor won't fall through the deck -- so now... more chores...
– David C. Rankin
Nov 25 '18 at 4:14
|
show 2 more comments
Wow Thank you So much for this extremely detailed and thorough answer. I was very confused about structs and this was very helpful!
– JustAJAVAGUY
Nov 24 '18 at 10:02
1
Sure, glad to help. Sometimes it just takes seeing a lookup-table to have it make sense. There are a lot of different applications for a simple lookup, generally to coordinate indexes between two different (but related) strutcs, etc.. Good luck with your coding.
– David C. Rankin
Nov 24 '18 at 10:33
Hi David, I wanted to ask you something, can you please start look at the message I've sent you on chat please
– JustAJAVAGUY
Nov 24 '18 at 10:51
1
drankinatty at gmail will do. - I'll be in and out today, I have some household chores to get to.... (oh joy....)
– David C. Rankin
Nov 24 '18 at 16:35
1
I have sent you a response -- got the trailer rebuilt, so now the tractor won't fall through the deck -- so now... more chores...
– David C. Rankin
Nov 25 '18 at 4:14
Wow Thank you So much for this extremely detailed and thorough answer. I was very confused about structs and this was very helpful!
– JustAJAVAGUY
Nov 24 '18 at 10:02
Wow Thank you So much for this extremely detailed and thorough answer. I was very confused about structs and this was very helpful!
– JustAJAVAGUY
Nov 24 '18 at 10:02
1
1
Sure, glad to help. Sometimes it just takes seeing a lookup-table to have it make sense. There are a lot of different applications for a simple lookup, generally to coordinate indexes between two different (but related) strutcs, etc.. Good luck with your coding.
– David C. Rankin
Nov 24 '18 at 10:33
Sure, glad to help. Sometimes it just takes seeing a lookup-table to have it make sense. There are a lot of different applications for a simple lookup, generally to coordinate indexes between two different (but related) strutcs, etc.. Good luck with your coding.
– David C. Rankin
Nov 24 '18 at 10:33
Hi David, I wanted to ask you something, can you please start look at the message I've sent you on chat please
– JustAJAVAGUY
Nov 24 '18 at 10:51
Hi David, I wanted to ask you something, can you please start look at the message I've sent you on chat please
– JustAJAVAGUY
Nov 24 '18 at 10:51
1
1
drankinatty at gmail will do. - I'll be in and out today, I have some household chores to get to.... (oh joy....)
– David C. Rankin
Nov 24 '18 at 16:35
drankinatty at gmail will do. - I'll be in and out today, I have some household chores to get to.... (oh joy....)
– David C. Rankin
Nov 24 '18 at 16:35
1
1
I have sent you a response -- got the trailer rebuilt, so now the tractor won't fall through the deck -- so now... more chores...
– David C. Rankin
Nov 25 '18 at 4:14
I have sent you a response -- got the trailer rebuilt, so now the tractor won't fall through the deck -- so now... more chores...
– David C. Rankin
Nov 25 '18 at 4:14
|
show 2 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53454699%2ftokening-an-external-file%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
This is at least closely related to How to assign values dynamically to a struct? . I've not yet worked out what's different from what was discussed there yesterday.
– Jonathan Leffler
Nov 24 '18 at 2:37
What is your output ? Could it be that your file has fields separated by
t
, that is not in your separators string ?– alamit
Nov 24 '18 at 2:43
1
This looks like a good job for simple look-up function that given
"TN"
returns an index where the information for Tennessee is stored instates
. Then it's a simple matter of reading"TN"
looking up the index, and writing the remaining data there.– David C. Rankin
Nov 24 '18 at 2:50
@alamit Look again.
– Swordfish
Nov 24 '18 at 3:18
1
It really doesn't make a whole lot of difference, but if you are dealing with climate data for the US states, then I would think you would want an array of 50
struct state
where each state has a pointer tostruct climate_info
. That way you coordinate all data by state with the data for each state within. If you were designing a database, then the records approach you are taking would make more sense since the database is designed to run "queries" against the information. But from a program standpoint it may make more sense to collect data by state. Worth considering.– David C. Rankin
Nov 24 '18 at 4:18