如何为结构体动态赋值
How to assign values dynamically to a struct
我对如何访问和更改结构的值感到困惑。该程序接收一些外部文件并对每个字符串进行标记化,并将它们分类在以下气候信息字段中。外部文件看起来像这样:
TDV 格式:
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
列的顺序是第一列是状态代码,第二列是时间戳(自 Unix 纪元以来的毫秒数),第三列是位置的 geohash 字符串(未使用),第四列是湿度百分比,第五个是降雪(值 0.0 或 1.0),第六个是云量百分比,第七个是雷击次数,第八个是压力(单位未知,但数据未使用,因此无关紧要)和第九是表面温度(以开尔文为单位)。我确实意识到我必须转换时间戳和表面温度,所以我并不担心。我需要在一个完整的状态下聚合数据(不管 geohash),跟踪最低和最高温度以及它们发生的时间,并计算该状态的记录数以便可以对值进行平均。
单个状态的输出应如下所示:
* Opening file: data_tn.tdv
* States found: TN
* -- State: TN --
* Number of Records: 17097
* Average Humidity: 49.4%
* Average Temperature: 58.3F
* Max Temperature: 110.4F on Mon Aug 3 11:00:00 2015
* Min Temperature: -11.1F on Fri Feb 20 04:00:00 2015
* Lightning Strikes: 781
* Records with Snow Cover: 107
* Average Cloud Cover: 53.0%
但是,会有多个状态,每个状态都有自己的数据文件要处理。
如您所见,第一个令牌将分配给州代码,但我不知道如何执行此操作。我已经尝试了许多 strcpy 和许多其他方法来尝试将令牌发送到各自的字段中,但是 none 已经奏效了。
struct climate_info
{
char code[3];
unsigned long num_records;
unsigned long timestamp;
char location[13];
unsigned int humidity;
int snow;
unsigned int cover;
int strikes;
long double pressure;
long double sum_temperature;
};
struct stats
{
char code[3];
long long timestamp;
double humidity;
double snow;
double cloud;
double strikes;
double sum_temperature;
}stats;
void analyze_file(FILE *file, struct climate_info *states[], int num_states);
void print_report(struct climate_info *states[], int num_states);
int main(int argc, char *argv[])
{
/* TODO: fix this conditional. You should be able to read multiple files. */
if (argc < 1 )
{
printf("Usage: %s tdv_file1 tdv_file2 ... tdv_fileN \n", argv[0]);
return EXIT_FAILURE;
}
/* Let's create an array to store our state data in. As we know, there are
* 50 US states. */
struct climate_info *states[NUM_STATES] = { NULL };
int i;
for (i = 1; i < argc; ++i)
{
/* TODO: Open the file for reading */
/* TODO: If the file doesn't exist, print an error message and move on
* to the next file. */
/* TODO: Analyze the file */
/* analyze_file(file, states, NUM_STATES); */
FILE *fp = fopen(argv[i], "r");
if(fp == NULL)
{
printf("Error opening file");
break;
}
else if(fp)
{
analyze_file(fp, states,NUM_STATES);
}
fclose(fp);
}
print_report(states, NUM_STATES);
return 0;
}
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 *y = malloc(sizeof(struct climate_info)*num_states);
token = strtok(line," \t");
strcpy((y[counter]).code,token);
counter++;
printf("%s\n",token);
while(token)
{
printf("token: %s\n", token);
token = strtok(NULL, " \t");
}
printf("%d\n",counter);
//free(states);
}
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");
不应将从文件中读取的值直接分配给结构的元素。您需要一组变量(它们可以在一个结构中,但不是必需的)来接收读取的数据,sscanf()
进行解析和拆分。然后验证状态代码是否正确、时间是否合理等等。然后你把累积的信息加到'statistics structure'中,它与你现在拥有的struct climate_info
相关但不同。例如,它不需要 geohash 列或压力列,但确实需要最低温度和被发现的时间,以及最高温度和被发现的时间。您可以累积积雪计数、雷击计数、湿度和云量以及当前温度。然后当你完成文件时,你可以平均温度、湿度和云量值,你可以打印聚合。
既然您明智地使用 fgets()
从文件中读取行(不要更改它!),您应该使用 sscanf()
来解析该行。您需要:
- 一个州代码 (
char state[3];
),
- 一个时间值(
long long millitime;
),
- 一个湿度值(
double humidity;
),
- 一个'snow present'值(
double snow;
因为格式是浮点数),
- 一个 'cloud cover' 值 (
double cloud;
),
- 一次雷击值(
double lightning
),
- 和温度值 (
double temperature;
)。
然后您使用
阅读它们
if (sscanf(line, "%2[A-Z] %lld %*s %lf %lf %lf %lf %*lf %lf",
state, &millitime, &humidity, &snow, &cloud, &lightning, &temperature) == 7)
{
…validate data and report errors if appropriate…
…stash values appropriately; increment the count…
}
else
{
…report format error?…
}
请注意,格式中的 *
会抑制赋值;该列已读取但被忽略。该代码检查压力是否为数字列;它不会验证超出 'it must exist' 的 geohash 列。可以将大小指定为上限 %*12s
.
使用 fgets()
和 sscanf()
的众多优点之一是您可以更清楚地报告错误 — 您可以说 "the state code was incorrect in line XXX:" 然后打印该行,因为您有它仍然可用。使用 fscanf()
,您将无法如此轻松地报告行的内容,这使得调试数据的人更加困难。
我对如何访问和更改结构的值感到困惑。该程序接收一些外部文件并对每个字符串进行标记化,并将它们分类在以下气候信息字段中。外部文件看起来像这样:
TDV 格式:
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
列的顺序是第一列是状态代码,第二列是时间戳(自 Unix 纪元以来的毫秒数),第三列是位置的 geohash 字符串(未使用),第四列是湿度百分比,第五个是降雪(值 0.0 或 1.0),第六个是云量百分比,第七个是雷击次数,第八个是压力(单位未知,但数据未使用,因此无关紧要)和第九是表面温度(以开尔文为单位)。我确实意识到我必须转换时间戳和表面温度,所以我并不担心。我需要在一个完整的状态下聚合数据(不管 geohash),跟踪最低和最高温度以及它们发生的时间,并计算该状态的记录数以便可以对值进行平均。
单个状态的输出应如下所示:
* Opening file: data_tn.tdv
* States found: TN
* -- State: TN --
* Number of Records: 17097
* Average Humidity: 49.4%
* Average Temperature: 58.3F
* Max Temperature: 110.4F on Mon Aug 3 11:00:00 2015
* Min Temperature: -11.1F on Fri Feb 20 04:00:00 2015
* Lightning Strikes: 781
* Records with Snow Cover: 107
* Average Cloud Cover: 53.0%
但是,会有多个状态,每个状态都有自己的数据文件要处理。
如您所见,第一个令牌将分配给州代码,但我不知道如何执行此操作。我已经尝试了许多 strcpy 和许多其他方法来尝试将令牌发送到各自的字段中,但是 none 已经奏效了。
struct climate_info
{
char code[3];
unsigned long num_records;
unsigned long timestamp;
char location[13];
unsigned int humidity;
int snow;
unsigned int cover;
int strikes;
long double pressure;
long double sum_temperature;
};
struct stats
{
char code[3];
long long timestamp;
double humidity;
double snow;
double cloud;
double strikes;
double sum_temperature;
}stats;
void analyze_file(FILE *file, struct climate_info *states[], int num_states);
void print_report(struct climate_info *states[], int num_states);
int main(int argc, char *argv[])
{
/* TODO: fix this conditional. You should be able to read multiple files. */
if (argc < 1 )
{
printf("Usage: %s tdv_file1 tdv_file2 ... tdv_fileN \n", argv[0]);
return EXIT_FAILURE;
}
/* Let's create an array to store our state data in. As we know, there are
* 50 US states. */
struct climate_info *states[NUM_STATES] = { NULL };
int i;
for (i = 1; i < argc; ++i)
{
/* TODO: Open the file for reading */
/* TODO: If the file doesn't exist, print an error message and move on
* to the next file. */
/* TODO: Analyze the file */
/* analyze_file(file, states, NUM_STATES); */
FILE *fp = fopen(argv[i], "r");
if(fp == NULL)
{
printf("Error opening file");
break;
}
else if(fp)
{
analyze_file(fp, states,NUM_STATES);
}
fclose(fp);
}
print_report(states, NUM_STATES);
return 0;
}
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 *y = malloc(sizeof(struct climate_info)*num_states);
token = strtok(line," \t");
strcpy((y[counter]).code,token);
counter++;
printf("%s\n",token);
while(token)
{
printf("token: %s\n", token);
token = strtok(NULL, " \t");
}
printf("%d\n",counter);
//free(states);
}
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");
不应将从文件中读取的值直接分配给结构的元素。您需要一组变量(它们可以在一个结构中,但不是必需的)来接收读取的数据,sscanf()
进行解析和拆分。然后验证状态代码是否正确、时间是否合理等等。然后你把累积的信息加到'statistics structure'中,它与你现在拥有的struct climate_info
相关但不同。例如,它不需要 geohash 列或压力列,但确实需要最低温度和被发现的时间,以及最高温度和被发现的时间。您可以累积积雪计数、雷击计数、湿度和云量以及当前温度。然后当你完成文件时,你可以平均温度、湿度和云量值,你可以打印聚合。
既然您明智地使用 fgets()
从文件中读取行(不要更改它!),您应该使用 sscanf()
来解析该行。您需要:
- 一个州代码 (
char state[3];
), - 一个时间值(
long long millitime;
), - 一个湿度值(
double humidity;
), - 一个'snow present'值(
double snow;
因为格式是浮点数), - 一个 'cloud cover' 值 (
double cloud;
), - 一次雷击值(
double lightning
), - 和温度值 (
double temperature;
)。
然后您使用
阅读它们if (sscanf(line, "%2[A-Z] %lld %*s %lf %lf %lf %lf %*lf %lf",
state, &millitime, &humidity, &snow, &cloud, &lightning, &temperature) == 7)
{
…validate data and report errors if appropriate…
…stash values appropriately; increment the count…
}
else
{
…report format error?…
}
请注意,格式中的 *
会抑制赋值;该列已读取但被忽略。该代码检查压力是否为数字列;它不会验证超出 'it must exist' 的 geohash 列。可以将大小指定为上限 %*12s
.
使用 fgets()
和 sscanf()
的众多优点之一是您可以更清楚地报告错误 — 您可以说 "the state code was incorrect in line XXX:" 然后打印该行,因为您有它仍然可用。使用 fscanf()
,您将无法如此轻松地报告行的内容,这使得调试数据的人更加困难。