一个自动定位并查询天气的工具(c语言)
一、代码
#include <stdio.h> // 引入标准输入输出库,用于打印和输入操作
#include <string.h> // 引入字符串处理库,用于字符串复制、查找等操作
#include <stdlib.h> // 引入标准库,用于内存分配(malloc/free)等函数
#include <sys/socket.h> // 引入socket相关库,用于网络通信
#include <netinet/in.h> // 引入网络地址结构库,用于定义IP地址和端口
#include <netdb.h> // 引入主机信息库,用于域名解析
#include <unistd.h> // 引入POSIX系统调用库,用于关闭文件描述符等操作
#include "cJSON.h" // 引入cJSON库,用于解析JSON数据extern int h_errno; // 声明外部变量h_errno,用于获取域名解析错误码// HTTP请求函数:向指定主机发送GET请求并返回JSON数据
char* http_get_json(const char* host, const char* path) {struct hostent *ht = gethostbyname(host); // 解析域名到IP地址(DNS解析)if (!ht) { // 如果解析失败,打印错误信息并返回NULLfprintf(stderr, "DNS解析失败: %s\n", hstrerror(h_errno));return NULL;}int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建TCP socket(IPv4协议,流式传输)if (sockfd == -1) { // 如果socket创建失败,打印错误并返回NULLperror("socket创建失败");return NULL;}// 初始化服务器地址结构struct sockaddr_in addr = {.sin_family = AF_INET, // 使用IPv4地址族.sin_port = htons(80), // 设置端口为80(HTTP默认端口),转换为网络字节序.sin_addr = *(struct in_addr *)ht->h_addr // 绑定解析得到的IP地址};// 连接服务器if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {perror("连接服务器失败"); // 连接失败时打印错误close(sockfd); // 关闭socket释放资源return NULL;}char request[1024]; // 定义HTTP请求缓冲区// 格式化HTTP GET请求内容(包含路径、主机等信息)snprintf(request, sizeof(request),"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", path, host);// 发送HTTP请求到服务器if (send(sockfd, request, strlen(request), 0) <= 0) {perror("发送请求失败"); // 发送失败时打印错误close(sockfd); // 关闭socketreturn NULL;}char *response = malloc(16384); // 分配内存存储服务器响应(16384字节)if (!response) { // 如果内存分配失败,打印错误并返回NULLperror("内存分配失败");close(sockfd);return NULL;}int total = 0, n; // total:已接收总字节数;n:每次接收的字节数// 循环接收服务器响应,直到接收完毕while ((n = recv(sockfd, response + total, 16384 - total - 1, 0)) > 0) {total += n; // 累加接收的字节数}response[total] = '\0'; // 给响应字符串添加结束符close(sockfd); // 关闭socketchar *json_start = strstr(response, "{"); // 找到第一个'{'作为JSON起始位置char *json_end = strrchr(response, '}'); // 找到最后一个'}'作为JSON结束位置if (!json_start || !json_end) { // 未找到JSON起止标志,释放内存返回NULLfree(response);return NULL;}size_t len = json_end - json_start + 2; // 计算JSON数据长度(包含结束符)char *json = malloc(len); // 为JSON数据分配独立内存if (!json) { // 内存分配失败,释放资源返回NULLfree(response);return NULL;}snprintf(json, len, "%s", json_start); // 复制提取的JSON数据到新缓冲区free(response); // 释放原始响应缓冲区return json; // 返回提取的JSON数据
}// 从att字段提取城市名(att格式:"国家,省份,城市")
char* extract_city(const char* att) {if (!att) return NULL; // 如果att为NULL,直接返回NULLchar *last_comma = strrchr(att, ','); // 找到最后一个逗号的位置(分割城市名)// 找到逗号返回逗号后子字符串(城市名),否则返回原始字符串return last_comma ? strdup(last_comma + 1) : strdup(att);
}// 获取本机IP对应的城市(通过IP查询API)
char* get_ip_city() {// 调用HTTP请求函数,获取IP定位的JSON响应char *json = http_get_json("api.k780.com", "/?app=ip.local&appkey=AppKey&sign=Sign&format=json");if (!json) return NULL; // 获取失败返回NULLcJSON *root = cJSON_Parse(json); // 解析JSON数据if (!root) { // 解析失败,释放内存返回NULLfree(json);return NULL;}cJSON *result = cJSON_GetObjectItem(root, "result"); // 提取"result"字段(IP定位信息)// 从result中提取"att"字段(格式:"国家,省份,城市")cJSON *att = result ? cJSON_GetObjectItem(result, "att") : NULL;char *city = att ? extract_city(att->valuestring) : NULL; // 调用extract_city提取城市名cJSON_Delete(root); // 释放cJSON解析内存free(json); // 释放JSON缓冲区return city; // 返回提取的城市名
}// 获取城市天气(通过天气API)
void get_weather(const char *city) {if (!city || strlen(city) == 0) { // 检查城市名是否有效(非NULL且非空)printf("无效的城市名称\n");return;}char path[1024]; // 定义天气API请求路径缓冲区// 格式化天气API路径,将城市名插入请求参数snprintf(path, sizeof(path),"/?app=weather.today&cityNm=%s&appkey=AppKey&sign=Sign&format=json", city);char *json = http_get_json("api.k780.com", path); // 获取天气JSON响应if (!json) { // 获取失败打印提示并返回printf("获取天气失败\n");return;}cJSON *root = cJSON_Parse(json); // 解析天气JSON数据if (!root) { // 解析失败释放内存返回printf("JSON解析失败\n");free(json);return;}cJSON *result = cJSON_GetObjectItem(root, "result"); // 提取"result"字段(天气信息)if (result) { // 天气数据存在,打印信息printf("\n%s天气信息:\n", city);cJSON *item; // 临时变量存储JSON字段if ((item = cJSON_GetObjectItem(result, "days"))) printf("日期: %s\n", item->valuestring); // 打印日期if ((item = cJSON_GetObjectItem(result, "weather"))) printf("天气: %s\n", item->valuestring); // 打印天气状况if ((item = cJSON_GetObjectItem(result, "temperature"))) printf("温度: %s\n", item->valuestring); // 打印温度if ((item = cJSON_GetObjectItem(result, "wind"))) printf("风向: %s\n", item->valuestring); // 打印风向if ((item = cJSON_GetObjectItem(result, "humidity"))) printf("湿度: %s\n", item->valuestring); // 打印湿度} else { // 未找到天气数据打印提示printf("未找到天气数据\n");}cJSON_Delete(root); // 释放cJSON内存free(json); // 释放JSON缓冲区
}// 主函数:程序入口
int main() {printf("正在获取本机位置...\n");char *city = get_ip_city(); // 调用get_ip_city获取本机IP对应的城市if (!city) { // 自动获取城市失败,手动输入printf("无法自动获取位置,请手动输入城市名称: ");city = malloc(100); // 分配内存存储手动输入的城市名fgets(city, 100, stdin); // 读取用户输入city[strcspn(city, "\n")] = '\0'; // 去除输入中的换行符} else { // 自动获取成功,打印城市名printf("自动定位到: %s\n", city);}get_weather(city); // 调用get_weather查询该城市的天气free(city); // 释放城市名字符串内存(避免泄漏)return 0; // 程序正常结束
}
把AppKey和Sign换成对应获取天气平台API的(各2处)
比如我用的是控制台 - NowAPI
二、编译
gcc 自动获取当地天气.c cJSON.c -o 自动获取当地天气 -lm
三、终端显示
四、整体功能说明
该程序是一个自动定位并查询天气的工具,核心功能流程如下:
- 自动定位城市:通过调用
api.k780.com
的 IP 查询 API,获取本机 IP 对应的地理位置信息(格式为 “国家,省份,城市”),并从中提取城市名。 - 手动输入降级:如果自动定位失败(如 API 不可用),程序会提示用户手动输入城市名。
- 查询天气信息:将获取到的城市名作为参数,调用
api.k780.com
的天气 API,获取并解析该城市的天气数据(日期、天气状况、温度、风向、湿度等),最后打印结果。
程序通过封装 HTTP 请求、JSON 解析、字符串处理等功能,实现了从 “IP 定位” 到 “天气查询” 的完整流程,同时包含了错误处理和内存管理,确保运行的稳定性。