一个程序通过 HTTP 协议调用天气 API,解析 JSON 格式的天气数据,提取关键信息并格式化输出:日期、天气状况、温度范围、风向、湿度等核心气象数据。
#include <stdio.h> // 标准输入输出库,提供printf、fgets等函数
#include <string.h> // 字符串处理库,提供strlen、strstr等函数
#include <sys/types.h> // 系统类型定义库,定义基本数据类型如pid_t等
#include <unistd.h> // Unix标准库,提供close、read、write等系统调用
#include <stdlib.h> // 标准库,提供exit、malloc等函数
#include <fcntl.h> // 文件控制库,定义文件操作相关常量和函数
#include <sys/stat.h> // 文件状态库,提供文件状态相关结构和函数
#include <sys/socket.h> // 套接字库,提供socket、connect等网络操作函数
#include <netinet/in.h> // 网络地址库,定义IPv4地址结构sockaddr_in等
#include <arpa/inet.h> // 网络地址转换库,提供inet_pton等地址转换函数
#include <netdb.h> // 网络数据库库,提供gethostbyname等域名解析函数
#include "cJSON.h" // cJSON库头文件,提供JSON解析和构建功能extern int h_errno; // 声明主机错误号,用于gethostbyname的错误处理int main() // 程序主函数,程序入口点
{char city[100] = {0}; // 定义存储城市名称的字符数组并初始化为0printf("请输入城市名称: "); // 打印提示信息,要求用户输入城市名称fgets(city, sizeof(city), stdin); // 从标准输入读取用户输入的城市名称,存入city数组city[strcspn(city, "\n")] = '\0'; // 移除输入字符串中的换行符,确保城市名称格式正确// 解析API域名,获取服务器主机信息struct hostent *ht = gethostbyname("api.k780.com");if(!ht){ // 判断域名解析是否成功fprintf(stderr,"域名解析失败: %s\n",strerror(h_errno)); // 输出解析错误信息exit(-1); // 解析失败,退出程序}// 创建socket,AF_INET为IPv4,SOCK_STREAM为TCP协议int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd == -1){ // 判断socket创建是否成功perror("socket创建失败"); // 输出创建失败信息exit(-1); // 创建失败,退出程序}// 准备服务器地址结构struct sockaddr_in addr; // 定义IPv4地址结构变量addr.sin_family = AF_INET; // 设置地址族为IPv4addr.sin_port = htons(80); // 设置端口号为80(HTTP默认端口),转换为网络字节序addr.sin_addr = *(struct in_addr *)ht->h_addr; // 设置服务器IP地址(从域名解析结果获取)// 连接到服务器,参数为socket描述符、服务器地址、地址长度int res = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));if(res == -1){ // 判断连接是否成功perror("连接服务器失败"); // 输出连接失败信息close(sockfd); // 关闭socketexit(-1); // 连接失败,退出程序}printf("connect OK!\n"); // 打印连接成功提示// 构造HTTP GET请求报文char protolHead[8192] = {0}; // 定义存储HTTP请求头的字符数组并初始化为0snprintf(protolHead, sizeof(protolHead), // 格式化构造HTTP请求"GET /?app=weather.today&cityNm=%s&appkey=AppKey&sign=Sign&format=json HTTP/1.1\r\n""Host: api.k780.com\r\n" // 指定主机名"Connection: close\r\n" // 关闭长连接,方便判断数据接收完毕"\r\n", city); // 请求参数包含城市名称,结尾空行分隔头部和正文// 发送HTTP请求到服务器res = send(sockfd, protolHead, strlen(protolHead), 0);if(res <= 0){ // 判断发送是否成功perror("发送请求失败"); // 输出发送失败信息close(sockfd); // 关闭socketexit(-1); // 发送失败,退出程序}// 循环接收服务器的完整响应数据char recvBuf[16384] = {0}; // 定义存储响应数据的缓冲区(扩大至16KB)int total_len = 0; // 记录接收数据的总长度while (1) {// 接收数据,存入缓冲区末尾res = recv(sockfd, recvBuf + total_len, sizeof(recvBuf) - total_len - 1, 0);if (res <= 0) break; // 接收完毕(返回0)或出错(返回-1),退出循环total_len += res; // 累加接收长度}recvBuf[total_len] = '\0'; // 手动添加字符串结束符// 提取并清理JSON数据(核心修正:处理多余字符)char *json_start = strstr(recvBuf, "{"); // 找到JSON数据的起始位置(第一个{)char *json_end = strrchr(recvBuf, '}'); // 找到JSON数据的结束位置(最后一个})if (!json_start || !json_end) { // 判断是否找到有效的JSON边界printf("未找到有效的JSON数据\n"); // 输出错误信息close(sockfd); // 关闭socketreturn -1; // 退出程序}json_end[1] = '\0'; // 在JSON结束位置后截断,去除多余字符// 打印清理后的JSON数据(调试用)printf("\n-------------------------------\n");printf("清理后的JSON数据:\n%s\n", json_start);printf("-------------------------------\n");// 解析JSON数据cJSON *root = cJSON_Parse(json_start);if (!root) { // 判断JSON解析是否成功printf("JSON解析失败: %s\n", cJSON_GetErrorPtr()); // 输出解析错误信息close(sockfd); // 关闭socketreturn -1; // 解析失败,退出程序}// 提取JSON中的关键字段cJSON *success = cJSON_GetObjectItem(root, "success"); // 提取API调用状态字段cJSON *msg = cJSON_GetObjectItem(root, "msg"); // 提取错误信息字段cJSON *result = cJSON_GetObjectItem(root, "result"); // 提取天气数据字段// 检查API调用是否成功(success是字符串"1"表示成功)if (success && strcmp(success->valuestring, "1") != 0) {if (msg) printf("API调用失败: %s\n", msg->valuestring); // 打印错误信息else printf("API调用失败: 未知错误\n");cJSON_Delete(root); // 释放JSON解析内存close(sockfd); // 关闭socketreturn -1; // 调用失败,退出程序}// 提取并打印天气信息if (result) { // 判断是否存在天气数据printf("\n===== %s 天气信息 =====\n", city); // 打印城市名称标题cJSON *days = cJSON_GetObjectItem(result, "days"); // 提取日期字段if (days) printf("日期: %s\n", days->valuestring); // 打印日期,否则提示未获取到else printf("日期: 未获取到\n");cJSON *weather = cJSON_GetObjectItem(result, "weather"); // 提取天气状况字段if (weather) printf("天气: %s\n", weather->valuestring);else printf("天气: 未获取到\n");cJSON *temperature = cJSON_GetObjectItem(result, "temperature"); // 提取温度字段if (temperature) printf("温度: %s\n", temperature->valuestring);else printf("温度: 未获取到\n");cJSON *wind = cJSON_GetObjectItem(result, "wind"); // 提取风向字段if (wind) printf("风向: %s\n", wind->valuestring);else printf("风向: 未获取到\n");cJSON *humidity = cJSON_GetObjectItem(result, "humidity"); // 提取湿度字段if (humidity) printf("湿度: %s\n", humidity->valuestring);else printf("湿度: 未获取到\n");} else {printf("未找到天气数据(result字段缺失)\n"); // 提示未找到数据}// 清理资源cJSON_Delete(root); // 释放cJSON解析占用的内存close(sockfd); // 关闭socket连接return 0; // 程序正常结束,返回0
}
把构造http请求报文中的第55行的AppKey和第56行的Sign换成对应获取天气平台API的
比如我用的是控制台 - NowAPI
编译
gcc 解析获取天气.c cJSON.c -o 解析获取天气 -lm
最终功能实现
程序通过 HTTP 协议调用天气 API,成功完成了:
- 接收用户输入的城市名称(如 “广州”);
- 与
api.k780.com
建立 TCP 连接并发送 HTTP 请求; - 接收并清理 API 响应中的无效字符(如前缀
21a
和后缀0
); - 解析 JSON 格式的天气数据,提取关键信息并格式化输出:
- 日期、天气状况、温度范围、风向、湿度等核心气象数据。
代码关键优化点
- JSON 数据净化:通过定位第一个
{
和最后一个}
,精准截取有效 JSON 片段,解决了响应中多余字符导致的解析失败问题。 - 类型兼容处理:针对 API 返回的
success: "1"
(字符串类型),改用字符串比较strcmp(success->valuestring, "1")
,避免类型判断错误。 - 字段映射修正:根据实际 JSON 结构,将温度字段从
temp
调整为temperature
,确保数据正确提取。 - 健壮性保障:对所有 JSON 字段增加空指针检查,避免因字段缺失导致程序崩溃(如
if (days) printf(...)
)。
运行结果说明
以 “广州” 为例,输出的天气信息清晰完整:
===== 广州 天气信息 =====
日期: 2025-08-07
天气: 雷阵雨转多云
温度: 33℃/26℃
风向: 南风
湿度: 100%
完全符合预期功能,代码可正常使用。如果需要扩展更多气象数据(如空气质量 aqi
、实时温度 temperature_curr
),只需参考现有逻辑添加对应字段的提取代码即可。...