重慶網(wǎng)站推百度網(wǎng)盤seo優(yōu)化
代碼概述
這段代碼實現(xiàn)了一個天氣查詢系統(tǒng),支持實時天氣、未來天氣和歷史天氣查詢。用戶可以通過終端菜單選擇查詢類型,并輸入城市名稱來獲取相應(yīng)的天氣信息。程序通過 TCP 連接發(fā)送 HTTP 請求,并解析返回的 JSON 數(shù)據(jù)來展示天氣信息。
#include "main.h"#include "cJSON.h"
#define BUFFER_SIZE 30000
#define TEMP_BUFFER_SIZE 4096#define COLOR_FIELD "\033[34m" // 字段藍(lán)色
#define COLOR_VALUE "\033[36m" // 值青色
#define COLOR_WARN "\033[33m" // 警告黃色
#define COLOR_RESET "\033[0m" // 重置顏色
#define COLOR_HEAD "\033[35m" // 提示頭顏色char weather_inquiry[4][1024] =
{{"實時天氣"},{"未來天氣"},{"歷史天氣"},{"退出查詢"},
};int pos_start = 0;
int pos_end = 4;
int focus = 0;//選擇的焦點
int menu_flag = 1;
char city[512];//儲存城市名字
int sockfd;int getch(void)
{struct termios oldt, newt;int ch;int esc_mode = 0;unsigned long key = 0;//獲取終端屬性信息tcgetattr(STDIN_FILENO, &oldt);newt = oldt;//設(shè)置非阻塞模式newt.c_lflag &= ~(ICANON | ECHO);newt.c_cc[VMIN] = 0;//讀取最小字符newt.c_cc[VTIME] = 1;//等待時間10/1秒//修改new中的ECHO和ICANON參數(shù),使得new為不回顯輸入內(nèi)容//設(shè)置終端信息tcsetattr(STDIN_FILENO, TCSANOW, &newt);//組合多字節(jié)按鍵for(int i = 0;i < 3;i++){ch = getchar();if(ch == EOF)break;key = (key << 8)|(ch & 0xFF);}//用完之后,恢復(fù)原來的終端屬性tcsetattr(STDIN_FILENO, TCSANOW, &oldt);switch(key){case 0x1B5B41: return KEY_UP;case 0x1B5B42: return KEY_DOWN;case 0x0A: return KEY_ENTER;default: return (key&0xFF);//返回首個有效字符 }
}void View_two(void)
{if(menu_flag == 2){int i = 0;printf("\033[10;35H");printf("| 天氣查詢 |\n");printf("\033[0m");for(i = pos_start;i <pos_end;i++){if(i == focus){printf("\033[%d;50H",i+12);printf("|\033[30;43m%2d.%s\033[0m\n",i+1,weather_inquiry[i]);}else{printf("\033[%d;50H",12+i);printf("|%2d.%-20s\n",i+1,weather_inquiry[i]);}}}return;
}void city_name(void)
{ scanf("%s",city);while(getchar()!= '\n');printf("\033[0m");if(strncmp(city,"quit",4) == 0){exit(0);printf("感謝使用!\n");}
}#if 1
void view(void)
{if(menu_flag == 1){printf("\033[15;55H");printf("\033[;31m");printf("輸入quit退出\n");printf("\033[10;25H");printf("\033[;33m");printf("~歡迎來到天氣查詢系統(tǒng)~\n");printf("\033[0m");printf("\033[12;25H");printf("\033[;34m");printf("請輸入你要查詢的城市:");// 清空城市名memset(city, 0, sizeof(city));city_name();menu_flag = 2;system("clear");}else if(menu_flag == 2){View_two();}return;
}#endifvoid CreateTcpClient(void)//創(chuàng)建TCP
{int ret = 0;sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd < 0){perror("sockfd fail");return;}struct sockaddr_in seraddr;bzero(&seraddr,sizeof(seraddr));seraddr.sin_family = AF_INET;seraddr.sin_port = htons(PORT);seraddr.sin_addr.s_addr = inet_addr(IP);if(connect(sockfd,(struct sockaddr*)&seraddr,sizeof(seraddr)) < 0){perror("connect fail");return;}
}//發(fā)送HTTP請求(參數(shù):天氣類型)
void SendHttpRequest(int sockfd,char *head)
{char tmpbuff[4096] = {0};int written = 0;const char *template ="GET %s HTTP/1.1\r\n""Host: api.k780.com\r\n""User-Agent: WeatherClient/1.0\r\n""Accept: */*\r\n""Connection: close\r\n" // 改為短連接"\r\n"; // 必須的空行// 安全格式化(限制最大長度)written = snprintf(tmpbuff,sizeof(tmpbuff),template,head);if (written >= sizeof(tmpbuff)) {fprintf(stderr, "Request too large (max %zd bytes)\n", sizeof(tmpbuff));return;}//發(fā)送請求ssize_t sent = send(sockfd,tmpbuff,written,0);if(sent != written){perror("send fail");return;}
}void RecvSendWeather(void)
{int fd = 0;char buff[BUFFER_SIZE] = {0}; // 確保初始化為全0char cmpbuff[BUFFER_SIZE] = {0}; // 用于存儲處理后的數(shù)據(jù)char tmpbuff[TEMP_BUFFER_SIZE] = {0}; // 臨時緩沖區(qū)ssize_t nsize = 0; // recv函數(shù)接收的數(shù)據(jù)大小char *ptmp = NULL; // 臨時指針char *pstart = NULL; // 數(shù)據(jù)起始位置指針char *pend = NULL; // 數(shù)據(jù)結(jié)束位置指針fd = open("recv.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);while(1){memset(tmpbuff, 0, sizeof(tmpbuff)); // 清空臨時緩沖區(qū)nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);if(nsize <= 0){break;}strncat(buff, tmpbuff, (size_t)nsize); // 將接收到的數(shù)據(jù)追加到buff中if(strstr(tmpbuff, "0\r\n") != NULL)break;}//查找數(shù)據(jù)正文的起始位置ptmp = strstr(buff, "\r\n\r\n");if(ptmp != NULL){ptmp += 4; //跳過"\r\n\r\n"ptmp = strstr(ptmp, "\r\n");if(ptmp != NULL){ptmp += 2;pstart = ptmp; //標(biāo)記數(shù)據(jù)起始位置pend = strstr(ptmp, "\r\n"); //查找數(shù)據(jù)結(jié)束位置if(pend != NULL){strncat(cmpbuff, pstart, pend - pstart);}}}write(fd, cmpbuff, strlen(cmpbuff)); // 將處理后的數(shù)據(jù)寫入文件close(fd);close(sockfd);
}void real_time_weather()
{int ret = -1;int fd = -1;char *buffer = NULL;ssize_t bytes_read = 0;CreateTcpClient(); // 創(chuàng)建新的連接if(!(buffer = malloc(BUFFER_SIZE))){perror("malloc fail");return;}if((fd = open("recv.txt", O_RDONLY)) == -1){perror("open fail");return;}if((bytes_read = read(fd, buffer, BUFFER_SIZE)) <= 0){fprintf(stderr, "Read failed or empty file\n");return;}buffer[bytes_read] = '\0'; // 確保字符終止cJSON *root = cJSON_Parse(buffer);if(!root){fprintf(stderr, "JSON parse failed\n");return;}// 使用結(jié)構(gòu)體定義字段映射struct FieldMapping {const char *json_key;const char *display_name;int required; // 1表示必須字段} fields[] = {{"days", "日期", 1},{"week", "星期", 1},{"citynm", "城市", 1},{"temperature_curr", "當(dāng)前溫度", 1},{"temp_high", "最高溫度", 0},{"temp_low", "最低溫度", 0},{"weather", "天氣狀況", 1},{"humidity", "當(dāng)前濕度", 0},{"humi_high", "最大濕度", 0},{"humi_low", "最小濕度", 0},{"wind", "風(fēng)向", 1},{"winp", "風(fēng)力", 1},{"aqi", "PM2.5", 0}};cJSON *result = cJSON_GetObjectItem(root, "result");if(!result){fprintf(stderr, "Missing result object\n");cJSON_Delete(root);return;}// 遍歷所有字段定義for(size_t i = 0; i < sizeof(fields)/sizeof(fields[0]); i++){cJSON *item = cJSON_GetObjectItem(result, fields[i].json_key);printf(COLOR_FIELD"%-8s: "COLOR_RESET, fields[i].display_name);if(item && item->valuestring){printf(COLOR_VALUE"%s\n"COLOR_RESET, item->valuestring);}else{printf(COLOR_WARN"N/A\n"COLOR_RESET);if(fields[i].required){fprintf(stderr, "Missing required field: %s\n", fields[i].json_key);cJSON_Delete(root);return;}}}ret = 0;free(buffer);cJSON_Delete(root);close(fd);
}void future_weather()
{int ret = -1;int fd = -1;char *buffer = NULL;ssize_t bytes_read = 0;CreateTcpClient(); // 創(chuàng)建新的連接if(!(buffer = malloc(BUFFER_SIZE))){perror("malloc fail");return;}if((fd = open("recv.txt",O_RDONLY)) == -1){perror("open fail");return;}if((bytes_read = read(fd,buffer,BUFFER_SIZE)) <= 0){fprintf(stderr, "Read failed or empty file\n");return;}buffer[bytes_read] = '\0';//確保字符終止cJSON *root = cJSON_Parse(buffer);if(!root){fprintf(stderr, "JSON parse failed\n");return;}// 使用結(jié)構(gòu)體定義字段映射struct FieldMapping {const char *json_key;const char *display_name;int required; // 1表示必須字段} fields[] = {{"days", "日期", 1},{"week", "星期", 1},{"citynm", "城市", 1},{"temp_high", "最高溫度", 0},{"temp_low", "最低溫度", 0},{"weather", "天氣狀況", 1},{"wind", "風(fēng)向", 1},{"winp", "風(fēng)力", 1},};cJSON *result = cJSON_GetObjectItem(root,"result");if(!result){fprintf(stderr, "Missing result object\n");cJSON_Delete(root);}int i = 0;cJSON *item;cJSON_ArrayForEach(item,result){printf(COLOR_HEAD"=============第%d天氣預(yù)報=============\n"COLOR_RESET,i+1);// 遍歷所有字段定義for (size_t j = 0; j < sizeof(fields)/sizeof(fields[0]); j++) {cJSON *field = cJSON_GetObjectItem(item, fields[j].json_key);printf(COLOR_FIELD"%-8s: "COLOR_RESET, fields[j].display_name);if (field && field->valuestring) {printf(COLOR_VALUE"%s\n"COLOR_RESET, field->valuestring);} else {printf(COLOR_WARN"N/A\n"COLOR_RESET);if (fields[j].required) {fprintf(stderr, "Missing required field: %s\n", fields[j].json_key);}}}i++;} cJSON_Delete(root);free(buffer);close(fd);
}void historical_weather()
{
}void MenuChoose(void)
{int key = getch();char tmpbuff[1024]={0};//ESC按鍵處理if(key == KEY_ESC){if(menu_flag == 2){menu_flag = 1;//切換到菜單一system("clear");}}switch(key){case KEY_UP:if(focus > pos_start){system("clear");focus--;}break;case KEY_DOWN:if(focus < pos_end){system("clear");focus++;}break;case KEY_ENTER:switch(focus){case 0:system("clear");CreateTcpClient();sprintf(tmpbuff,"/?app=weather.today&weaid=%s&appkey=%s&sign=%s",city,API_KEY,API_SIGN);SendHttpRequest(sockfd,tmpbuff);RecvSendWeather();real_time_weather();if (sockfd > 0) {close(sockfd);sockfd = -1;}break;case 1:system("clear");CreateTcpClient();sprintf(tmpbuff,"http://api.k780.com/?app=weather.future&weaid=%s&appkey=%s&sign=%s&format=json",city,API_KEY,API_SIGN);SendHttpRequest(sockfd,tmpbuff);RecvSendWeather();future_weather();break;case 2:break;case 3:printf("感謝使用!\n");exit(0);break;} }
}int main(int argc, const char *argv[])
{system("clear");while(1){view();MenuChoose();}return 0;
}
演示視頻
天氣查詢演示視頻
一、函數(shù)總覽
全局變量
-
weather_inquiry[4][1024]
:存儲菜單選項,包括實時天氣、未來天氣、歷史天氣和退出查詢。 -
pos_start
和pos_end
:定義菜單顯示的起始和結(jié)束位置。 -
focus
:當(dāng)前用戶選擇的菜單焦點。 -
menu_flag
:標(biāo)志當(dāng)前顯示的菜單層級,1
表示主菜單,2
表示二級菜單。 -
city[512]
:存儲用戶輸入的城市名稱。 -
sockfd
:TCP 套接字文件描述符。
第二部分:輸入處理和菜單顯示
getch()
?函數(shù)
-
功能:捕捉用戶按鍵輸入,支持方向鍵和回車鍵。
-
實現(xiàn):
-
使用
termios
修改終端屬性,禁用回顯和行緩沖。 -
捕捉按鍵組合,識別上方向鍵(
KEY_UP
)、下方向鍵(KEY_DOWN
)和回車鍵(KEY_ENTER
)。 -
恢復(fù)終端屬性。
-
View_two()
?函數(shù)
-
功能:顯示二級菜單,包括實時天氣、未來天氣、歷史天氣和退出查詢。
-
實現(xiàn):
-
根據(jù)
focus
的值,高亮顯示當(dāng)前選中的菜單項。 -
使用終端控制字符(如
\033
)設(shè)置文字顏色和位置。
-
city_name()
?函數(shù)
-
功能:獲取用戶輸入的城市名稱,并檢查是否為退出命令。
-
實現(xiàn):
-
使用
scanf
獲取輸入,并清除輸入緩沖區(qū)。 -
如果輸入為
quit
,程序退出。
-
view()
?函數(shù)
-
功能:根據(jù)
menu_flag
的值,顯示主菜單或二級菜單。 -
實現(xiàn):
-
主菜單顯示歡迎信息,并提示用戶輸入城市名稱。
-
二級菜單顯示天氣查詢選項。
-
第三部分:TCP 連接和 HTTP 請求
CreateTcpClient()
?函數(shù)
-
功能:創(chuàng)建一個 TCP 套接字,并連接到指定的服務(wù)器。
-
實現(xiàn):
-
使用
socket
創(chuàng)建套接字。 -
設(shè)置服務(wù)器地址和端口。
-
使用
connect
建立連接。
-
SendHttpRequest()
?函數(shù)
-
功能:發(fā)送 HTTP GET 請求。
-
實現(xiàn):
-
使用
snprintf
格式化請求頭,包括目標(biāo) URL 和必要頭部信息。 -
使用
send
發(fā)送請求。
-
第四部分:數(shù)據(jù)接收和處理
RecvSendWeather()
?函數(shù)
-
功能:接收服務(wù)器響應(yīng),并提取數(shù)據(jù)正文。
-
實現(xiàn):
-
使用
recv
循環(huán)接收數(shù)據(jù),直到遇到空行(0\r\n
)。 -
查找數(shù)據(jù)正文的起始和結(jié)束位置,提取并存儲到文件中。
-
第五部分:實時天氣查詢
real_time_weather()
?函數(shù)
-
功能:解析實時天氣 JSON 數(shù)據(jù),并展示相關(guān)信息。
-
實現(xiàn):
-
從文件讀取 JSON 數(shù)據(jù)。
-
使用
cJSON
解析 JSON 數(shù)據(jù)。 -
定義字段映射結(jié)構(gòu)體,遍歷字段并顯示對應(yīng)值。
-
檢查必填字段是否存在,若缺失則報錯。
-
第六部分:未來天氣查詢
future_weather()
?函數(shù)
-
功能:解析未來天氣 JSON 數(shù)據(jù),并展示相關(guān)信息。
-
實現(xiàn):
-
從文件讀取 JSON 數(shù)據(jù)。
-
使用
cJSON
解析 JSON 數(shù)據(jù)。 -
定義字段映射結(jié)構(gòu)體,遍歷字段并顯示對應(yīng)值。
-
遍歷 JSON 數(shù)組,逐條顯示未來天氣信息。
-
第七部分:菜單選擇和主程序
MenuChoose()
?函數(shù)
-
功能:處理用戶按鍵輸入,實現(xiàn)菜單導(dǎo)航和功能選擇。
-
實現(xiàn):
-
根據(jù)按鍵調(diào)整
focus
值,實現(xiàn)菜單上下導(dǎo)航。 -
處理回車鍵,根據(jù)選中項執(zhí)行對應(yīng)功能:
-
實時天氣查詢:構(gòu)造 URL 并發(fā)送請求,調(diào)用
real_time_weather
。 -
未來天氣查詢:構(gòu)造 URL 并發(fā)送請求,調(diào)用
future_weather
。 -
退出程序:退出并顯示感謝信息。
-
-
main()
?函數(shù)
-
功能:主程序入口,循環(huán)顯示菜單并處理用戶輸入。
-
實現(xiàn):
-
清屏并初始化終端。
-
循環(huán)調(diào)用
view
和MenuChoose
,實現(xiàn)菜單顯示和交互。
-
二、重點函數(shù)
RecvSendWeather()
?函數(shù)
void RecvSendWeather(void)
{int fd = 0;char buff[BUFFER_SIZE] = {0}; // 確保初始化為全0char cmpbuff[BUFFER_SIZE] = {0}; // 用于存儲處理后的數(shù)據(jù)char tmpbuff[TEMP_BUFFER_SIZE] = {0}; // 臨時緩沖區(qū)ssize_t nsize = 0; // recv函數(shù)接收的數(shù)據(jù)大小char *ptmp = NULL; // 臨時指針char *pstart = NULL; // 數(shù)據(jù)起始位置指針char *pend = NULL; // 數(shù)據(jù)結(jié)束位置指針fd = open("recv.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);while(1){memset(tmpbuff, 0, sizeof(tmpbuff)); // 清空臨時緩沖區(qū)nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);if(nsize <= 0){break;}strncat(buff, tmpbuff, (size_t)nsize); // 將接收到的數(shù)據(jù)追加到buff中if(strstr(tmpbuff, "0\r\n") != NULL)break;}//查找數(shù)據(jù)正文的起始位置ptmp = strstr(buff, "\r\n\r\n");if(ptmp != NULL){ptmp += 4; //跳過"\r\n\r\n"ptmp = strstr(ptmp, "\r\n");if(ptmp != NULL){ptmp += 2;pstart = ptmp; //標(biāo)記數(shù)據(jù)起始位置pend = strstr(ptmp, "\r\n"); //查找數(shù)據(jù)結(jié)束位置if(pend != NULL){strncat(cmpbuff, pstart, pend - pstart);}}}write(fd, cmpbuff, strlen(cmpbuff)); // 將處理后的數(shù)據(jù)寫入文件close(fd);close(sockfd);
}
1. 初始化與文件準(zhǔn)備
- 定義緩沖區(qū)
buff
(存儲完整數(shù)據(jù))、cmpbuff
(存儲處理后的數(shù)據(jù))和tmpbuff
(臨時接收數(shù)據(jù))。 - 創(chuàng)建/清空文件
recv.txt
用于保存最終數(shù)據(jù)。
2. 循環(huán)接收數(shù)據(jù)
- 通過
recv
從套接字sockfd
接收數(shù)據(jù)到tmpbuff
。 - 將每次接收的數(shù)據(jù)追加到主緩沖區(qū)
buff
中。 - 終止條件:當(dāng)收到包含
"0\r\n"
的數(shù)據(jù)時,認(rèn)為傳輸結(jié)束,退出循環(huán)。
3. 數(shù)據(jù)解析
- 定位數(shù)據(jù)起始點:
- 查找首個
\r\n\r\n
,跳過HTTP頭部。 - 繼續(xù)查找下一個
\r\n
,跳過可能的分塊長度字段(如5\r\n
)。
- 查找首個
- 提取數(shù)據(jù)正文:
- 從
pstart
到下一個\r\n
之間的內(nèi)容被視為有效數(shù)據(jù),復(fù)制到cmpbuff
。
- 從
4. 保存與清理
- 將處理后的數(shù)據(jù)寫入文件
recv.txt
。 - 關(guān)閉文件描述符和套接字。
潛在問題與改進(jìn)建議
-
緩沖區(qū)溢出風(fēng)險
- 問題:
buff
和cmpbuff
是固定大小,未檢查strncat
是否越界。 - 建議:增加長度檢查,或改用動態(tài)內(nèi)存分配。
- 問題:
-
分塊編碼處理不完整
- 問題:代碼假設(shè)數(shù)據(jù)僅包含一個分塊(如
5\r\nhello\r\n0\r\n
),可能漏掉多分塊數(shù)據(jù)。 - 建議:循環(huán)解析所有分塊,處理格式如
長度\r\n數(shù)據(jù)\r\n
。
- 問題:代碼假設(shè)數(shù)據(jù)僅包含一個分塊(如
-
結(jié)束標(biāo)志可靠性
- 問題:若
0\r\n
被拆分成多次接收(如0\r
和\n
),可能無法正確退出。 - 建議:在
buff
中全局搜索0\r\n
,而非僅檢查tmpbuff
。
- 問題:若
real_time_weather()
?函數(shù)
void real_time_weather()
{int ret = -1;int fd = -1;char *buffer = NULL;ssize_t bytes_read = 0;CreateTcpClient(); // 創(chuàng)建新的連接if(!(buffer = malloc(BUFFER_SIZE))){perror("malloc fail");return;}if((fd = open("recv.txt", O_RDONLY)) == -1){perror("open fail");return;}if((bytes_read = read(fd, buffer, BUFFER_SIZE)) <= 0){fprintf(stderr, "Read failed or empty file\n");return;}buffer[bytes_read] = '\0'; // 確保字符終止cJSON *root = cJSON_Parse(buffer);if(!root){fprintf(stderr, "JSON parse failed\n");return;}// 使用結(jié)構(gòu)體定義字段映射struct FieldMapping {const char *json_key;const char *display_name;int required; // 1表示必須字段} fields[] = {{"days", "日期", 1},{"week", "星期", 1},{"citynm", "城市", 1},{"temperature_curr", "當(dāng)前溫度", 1},{"temp_high", "最高溫度", 0},{"temp_low", "最低溫度", 0},{"weather", "天氣狀況", 1},{"humidity", "當(dāng)前濕度", 0},{"humi_high", "最大濕度", 0},{"humi_low", "最小濕度", 0},{"wind", "風(fēng)向", 1},{"winp", "風(fēng)力", 1},{"aqi", "PM2.5", 0}};cJSON *result = cJSON_GetObjectItem(root, "result");if(!result){fprintf(stderr, "Missing result object\n");cJSON_Delete(root);return;}// 遍歷所有字段定義for(size_t i = 0; i < sizeof(fields)/sizeof(fields[0]); i++){cJSON *item = cJSON_GetObjectItem(result, fields[i].json_key);printf(COLOR_FIELD"%-8s: "COLOR_RESET, fields[i].display_name);if(item && item->valuestring){printf(COLOR_VALUE"%s\n"COLOR_RESET, item->valuestring);}else{printf(COLOR_WARN"N/A\n"COLOR_RESET);if(fields[i].required){fprintf(stderr, "Missing required field: %s\n", fields[i].json_key);cJSON_Delete(root);return;}}}ret = 0;free(buffer);cJSON_Delete(root);close(fd);
}
亮點分析補(bǔ)充與優(yōu)化建議
1.?模塊化設(shè)計
- 優(yōu)勢:
將天氣查詢功能封裝為獨(dú)立函數(shù)real_time_weather()
,邏輯邊界清晰,符合“高內(nèi)聚”原則。 - 改進(jìn)空間:
- 單一職責(zé)原則:當(dāng)前函數(shù)承擔(dān)了網(wǎng)絡(luò)連接、文件操作、JSON解析、數(shù)據(jù)展示等多個職責(zé),可進(jìn)一步拆分為子函數(shù)(如
fetch_weather_data()
、parse_json()
、display_weather()
)。 - 依賴解耦:文件
recv.txt
作為數(shù)據(jù)傳遞媒介,屬于硬編碼依賴,建議改用內(nèi)存直接傳遞(如通過函數(shù)參數(shù)傳遞數(shù)據(jù)緩沖區(qū))。
- 單一職責(zé)原則:當(dāng)前函數(shù)承擔(dān)了網(wǎng)絡(luò)連接、文件操作、JSON解析、數(shù)據(jù)展示等多個職責(zé),可進(jìn)一步拆分為子函數(shù)(如
2.?動態(tài)內(nèi)存管理
- 優(yōu)勢:
使用malloc
動態(tài)分配內(nèi)存,避免棧溢出風(fēng)險,適應(yīng)大數(shù)據(jù)場景。 - 改進(jìn)空間:
- 內(nèi)存泄漏風(fēng)險:錯誤處理路徑中未完全釋放內(nèi)存(如
malloc
成功但open
失敗時未free(buffer)
),需用goto
或cleanup
標(biāo)簽統(tǒng)一釋放資源。 - 緩沖區(qū)溢出:
read(fd, buffer, BUFFER_SIZE)
未檢查bytes_read
是否等于BUFFER_SIZE
,可能導(dǎo)致數(shù)據(jù)截斷或越界。
- 內(nèi)存泄漏風(fēng)險:錯誤處理路徑中未完全釋放內(nèi)存(如
3.?錯誤處理
- 優(yōu)勢:
覆蓋文件操作、內(nèi)存分配、JSON解析等關(guān)鍵錯誤點,避免程序崩潰。 - 改進(jìn)空間:
- 錯誤碼統(tǒng)一管理:
ret
變量未被實際使用,建議通過返回值或全局錯誤碼明確錯誤類型。 - 錯誤信息分級:區(qū)分“致命錯誤”(如必填字段缺失)與“警告信息”(如可選字段缺失),避免冗余報錯。
- 錯誤碼統(tǒng)一管理:
4.?數(shù)據(jù)解析
- 優(yōu)勢:
使用cJSON
庫解析復(fù)雜JSON數(shù)據(jù),通過結(jié)構(gòu)體FieldMapping
實現(xiàn)字段映射,擴(kuò)展性強(qiáng)。 - 改進(jìn)空間:
- 字段類型校驗:未檢查JSON字段實際類型(如數(shù)值型字段可能被誤讀為字符串),需增加
cJSON_IsString
/cJSON_IsNumber
等校驗。 - 嵌套結(jié)構(gòu)處理:若JSON層級更深(如
result
下包含嵌套對象),需支持遞歸解析。
- 字段類型校驗:未檢查JSON字段實際類型(如數(shù)值型字段可能被誤讀為字符串),需增加
5.?用戶輸出
- 優(yōu)勢:
ANSI顏色代碼提升可讀性,字段映射表確保輸出完整性。 - 改進(jìn)空間:
- 跨平臺兼容性:Windows終端默認(rèn)不支持ANSI顏色,需通過條件編譯適配。
- 格式化輸出:使用固定寬度(
%-8s
)可能導(dǎo)致長字段顯示不全,建議動態(tài)調(diào)整列寬。
難點分析與解決方案
1.?JSON數(shù)據(jù)解析
- 核心挑戰(zhàn):
- 字段路徑依賴性強(qiáng)(如直接訪問
root->result->temperature_curr
),若服務(wù)器返回結(jié)構(gòu)變化,需修改代碼。 - 未處理特殊字符(如轉(zhuǎn)義字符
\"
)或Unicode編碼(如\u4e2d\u6587
)。
- 字段路徑依賴性強(qiáng)(如直接訪問
- 解決方案:
- 使用
cJSON_GetObjectItemCaseSensitive
避免大小寫敏感問題。 - 通過
cJSON_Print
標(biāo)準(zhǔn)化輸出,確保轉(zhuǎn)義字符正確處理。
- 使用
2.?網(wǎng)絡(luò)通信
- 核心挑戰(zhàn):
CreateTcpClient()
實現(xiàn)未展示,可能隱藏連接超時、重試等邏輯缺失。- 未處理HTTP協(xié)議細(xì)節(jié)(如狀態(tài)碼檢查、分塊傳輸編碼)。
- 解決方案:
- 封裝HTTP客戶端庫(如
libcurl
)簡化網(wǎng)絡(luò)操作。 - 增加超時機(jī)制和重試邏輯,提升魯棒性。
- 封裝HTTP客戶端庫(如
3.?文件操作
- 核心挑戰(zhàn):
- 文件讀寫與網(wǎng)絡(luò)通信耦合,可能導(dǎo)致數(shù)據(jù)不同步(如文件未及時刷新)。
- 解決方案:
- 使用內(nèi)存映射文件(
mmap
)或管道(pipe
)替代臨時文件。 - 增加文件鎖(
flock
)避免多進(jìn)程競爭。
- 使用內(nèi)存映射文件(
future_weather()
?函數(shù)
void future_weather()
{int ret = -1;int fd = -1;char *buffer = NULL;ssize_t bytes_read = 0;CreateTcpClient(); // 創(chuàng)建新的連接if(!(buffer = malloc(BUFFER_SIZE))){perror("malloc fail");return;}if((fd = open("recv.txt",O_RDONLY)) == -1){perror("open fail");return;}if((bytes_read = read(fd,buffer,BUFFER_SIZE)) <= 0){fprintf(stderr, "Read failed or empty file\n");return;}buffer[bytes_read] = '\0';//確保字符終止cJSON *root = cJSON_Parse(buffer);if(!root){fprintf(stderr, "JSON parse failed\n");return;}// 使用結(jié)構(gòu)體定義字段映射struct FieldMapping {const char *json_key;const char *display_name;int required; // 1表示必須字段} fields[] = {{"days", "日期", 1},{"week", "星期", 1},{"citynm", "城市", 1},{"temp_high", "最高溫度", 0},{"temp_low", "最低溫度", 0},{"weather", "天氣狀況", 1},{"wind", "風(fēng)向", 1},{"winp", "風(fēng)力", 1},};cJSON *result = cJSON_GetObjectItem(root,"result");if(!result){fprintf(stderr, "Missing result object\n");cJSON_Delete(root);}int i = 0;cJSON *item;cJSON_ArrayForEach(item,result){printf(COLOR_HEAD"=============第%d天氣預(yù)報=============\n"COLOR_RESET,i+1);// 遍歷所有字段定義for (size_t j = 0; j < sizeof(fields)/sizeof(fields[0]); j++) {cJSON *field = cJSON_GetObjectItem(item, fields[j].json_key);printf(COLOR_FIELD"%-8s: "COLOR_RESET, fields[j].display_name);if (field && field->valuestring) {printf(COLOR_VALUE"%s\n"COLOR_RESET, field->valuestring);} else {printf(COLOR_WARN"N/A\n"COLOR_RESET);if (fields[j].required) {fprintf(stderr, "Missing required field: %s\n", fields[j].json_key);}}}i++;} cJSON_Delete(root);free(buffer);close(fd);
}
功能概述
該函數(shù)用于獲取并展示未來多天的天氣預(yù)報數(shù)據(jù),整體流程如下:
- 建立TCP連接獲取數(shù)據(jù)(依賴
CreateTcpClient()
)。 - 從文件
recv.txt
讀取JSON格式的天氣數(shù)據(jù)。 - 解析JSON數(shù)據(jù)并格式化輸出未來多天的天氣信息。
亮點與改進(jìn)建議
1. 核心亮點
-
數(shù)據(jù)遍歷邏輯
使用cJSON_ArrayForEach
遍歷天氣預(yù)報的多個結(jié)果,支持動態(tài)數(shù)量的天氣預(yù)報條目,靈活性較高。cJSON_ArrayForEach(item, result) {printf("第%d天天氣預(yù)報", i+1);// 遍歷字段并輸出 }
-
字段映射復(fù)用
復(fù)用FieldMapping
結(jié)構(gòu)體定義字段映射關(guān)系,與real_time_weather()
保持一致性,降低維護(hù)成本。 -
用戶交互友好
通過顏色區(qū)分標(biāo)題、字段名和數(shù)值,輸出層次清晰:printf(COLOR_HEAD"=============第%d天氣預(yù)報=============\n"COLOR_RESET, i+1);
2. 潛在問題與改進(jìn)
問題1:內(nèi)存與資源泄漏
-
風(fēng)險點
- 文件打開失敗(
open
返回-1
)時,未釋放malloc
分配的buffer
。 - JSON解析失敗時,未關(guān)閉文件描述符
fd
。
- 文件打開失敗(
-
修復(fù)建議
使用統(tǒng)一清理邏輯確保資源釋放:void future_weather() {int fd = -1;char *buffer = NULL;cJSON *root = NULL;// 初始化代碼...// 錯誤處理跳轉(zhuǎn)標(biāo)簽cleanup:if (buffer) free(buffer);if (root) cJSON_Delete(root);if (fd != -1) close(fd); }
問題2:JSON類型安全性
-
風(fēng)險點
直接訪問field->valuestring
,若字段值為數(shù)值類型(如溫度),會導(dǎo)致空指針或錯誤輸出。// 錯誤示例:若temp_high為數(shù)值,valuestring為NULL printf("%s", field->valuestring);
-
修復(fù)建議
增加類型檢查,支持?jǐn)?shù)值和字符串:if (cJSON_IsString(field)) {printf("%s", field->valuestring); } else if (cJSON_IsNumber(field)) {printf("%d", field->valueint); }
問題3:數(shù)據(jù)完整性風(fēng)險
-
風(fēng)險點
假設(shè)result
為數(shù)組類型,若服務(wù)器返回非數(shù)組數(shù)據(jù)(如空對象),程序會錯誤遍歷。cJSON *result = cJSON_GetObjectItem(root, "result"); cJSON_ArrayForEach(item, result); // 若result非數(shù)組,崩潰!
-
修復(fù)建議
驗證數(shù)據(jù)類型:if (!cJSON_IsArray(result)) {fprintf(stderr, "Invalid result format: expected array\n");goto cleanup; }
問題4:代碼冗余
-
風(fēng)險點
future_weather
與real_time_weather
存在大量重復(fù)代碼(如文件操作、JSON解析),違反DRY原則。 -
修復(fù)建議
抽象公共邏輯為獨(dú)立函數(shù):// 公共函數(shù):讀取文件到緩沖區(qū) char* read_weather_data(const char *filename) {int fd = open(filename, O_RDONLY);// 讀取并返回buffer... }// 公共函數(shù):解析并打印天氣字段 void print_weather_fields(cJSON *item, struct FieldMapping *fields, size_t count) {// 遍歷字段并輸出... }
難點與解決方案
1. 動態(tài)數(shù)組遍歷
- 難點
需處理未知數(shù)量的天氣預(yù)報條目,且每個條目需完整解析字段。 - 方案
使用cJSON_ArrayForEach
宏安全遍歷數(shù)組,避免手動索引越界。
2. 多層級JSON解析
- 難點
若JSON結(jié)構(gòu)復(fù)雜(如嵌套對象),需遞歸解析。 - 方案
設(shè)計遞歸解析函數(shù),處理嵌套結(jié)構(gòu):void parse_nested(cJSON *node, int depth) {if (cJSON_IsObject(node)) {cJSON_ArrayForEach(child, node) {parse_nested(child, depth + 1);}} }