一、代码
#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) { // 如果解析失败,打印错误信息并返回NULL
fprintf(stderr, "DNS解析失败: %s\n", hstrerror(h_errno));
return NULL;
}
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建TCP socket(IPv4协议,流式传输)
if (sockfd == -1) { // 如果socket创建失败,打印错误并返回NULL
perror("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); // 关闭socket
return NULL;
}
char *response = malloc(16384); // 分配内存存储服务器响应(16384字节)
if (!response) { // 如果内存分配失败,打印错误并返回NULL
perror("内存分配失败");
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); // 关闭socket
char *json_start = strstr(response, "{"); // 找到第一个'{'作为JSON起始位置
char *json_end = strrchr(response, '}'); // 找到最后一个'}'作为JSON结束位置
if (!json_start || !json_end) { // 未找到JSON起止标志,释放内存返回NULL
free(response);
return NULL;
}
size_t len = json_end - json_start + 2; // 计算JSON数据长度(包含结束符)
char *json = malloc(len); // 为JSON数据分配独立内存
if (!json) { // 内存分配失败,释放资源返回NULL
free(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,直接返回NULL
char *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; // 获取失败返回NULL
cJSON *root = cJSON_Parse(json); // 解析JSON数据
if (!root) { // 解析失败,释放内存返回NULL
free(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 定位” 到 “天气查询” 的完整流程,同时包含了错误处理和内存管理,确保运行的稳定性。