当前位置: 首页 > news >正文

一个程序通过 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,成功完成了:

  1. 接收用户输入的城市名称(如 “广州”);
  2. 与 api.k780.com 建立 TCP 连接并发送 HTTP 请求;
  3. 接收并清理 API 响应中的无效字符(如前缀 21a 和后缀 0);
  4. 解析 JSON 格式的天气数据,提取关键信息并格式化输出:
    • 日期、天气状况、温度范围、风向、湿度等核心气象数据。

代码关键优化点

  1. JSON 数据净化:通过定位第一个 { 和最后一个 },精准截取有效 JSON 片段,解决了响应中多余字符导致的解析失败问题。
  2. 类型兼容处理:针对 API 返回的 success: "1"(字符串类型),改用字符串比较 strcmp(success->valuestring, "1"),避免类型判断错误。
  3. 字段映射修正:根据实际 JSON 结构,将温度字段从 temp 调整为 temperature,确保数据正确提取。
  4. 健壮性保障:对所有 JSON 字段增加空指针检查,避免因字段缺失导致程序崩溃(如 if (days) printf(...))。

运行结果说明

以 “广州” 为例,输出的天气信息清晰完整:

===== 广州 天气信息 =====
日期: 2025-08-07
天气: 雷阵雨转多云
温度: 33℃/26℃
风向: 南风
湿度: 100%

完全符合预期功能,代码可正常使用。如果需要扩展更多气象数据(如空气质量 aqi、实时温度 temperature_curr),只需参考现有逻辑添加对应字段的提取代码即可。...

http://www.dtcms.com/a/320179.html

相关文章:

  • 1688 商品详情接口开发实战:从平台特性到高可用实现
  • Redis最新安装教程(WindowsLinux)
  • 对基带信号进行调制的原因及通俗理解
  • HR人才测评工具,卡特尔16pf性格测试
  • Numpy科学计算与数据分析:Numpy数学函数入门与实践
  • 我爱发明之Linux下使用Conky在桌面显示Spotify状态及封面字符画
  • 无损音乐下载器!(电脑)绿色免费,无限下载,无损音质
  • 是否将标签页tag信息存储在Redux store中还是仅存储在hook的state中
  • AI题解5
  • 什么是0.5米分辨率卫星影像数据?
  • 一文学会c++继承 组合
  • [优选算法专题一双指针——两数之和](双指针和哈希表)
  • 解决GitHub push失败-Failed to connect to github.com port 443: Timed out
  • 亚马逊卖家反馈机制变革:纯星级评级时代的合规挑战与运营重构
  • SOMGAN:用自组织映射改善GAN的模式探索能力
  • 自然语言处理×第四卷:文本特征与数据——她开始准备:每一次输入,都是为了更像你地说话
  • python selenium环境安装
  • Python自动化测试selenium指定截图文件名方法
  • MySQL 备份利器 Xtrabackup 全解析:从部署到恢复的实战指南
  • 视觉语言模型的空间推理缺陷——AI 在医学扫描中难以区分左右
  • 《CogAgent: A Visual Language Model for GUI Agents》论文精读笔记
  • Vue 3 入门教程 9 - 表单处理
  • 8、Redis的HyperLogLog、事务Multi、管道Pipeline,以及Redis7.0特性
  • DoubleTrouble靶机
  • 【R语言】重新绘制高清MaxEnt的单因素响应曲线图像
  • 最佳左前缀法则(Optimal Left-Prefix Rule)
  • 【Dijkstra】 Shortest Routes I
  • 5种将Android联系人传输到电脑的方法
  • 《C语言》函数练习题--4
  • Debain12 api方式部署redis服务