一个自动定位并查询天气的工具(c语言)

发布于:2025-08-09 ⋅ 阅读:(14) ⋅ 点赞:(0)

一、代码

#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

三、终端显示

四、整体功能说明

该程序是一个自动定位并查询天气的工具,核心功能流程如下:

  1. 自动定位城市:通过调用api.k780.com的 IP 查询 API,获取本机 IP 对应的地理位置信息(格式为 “国家,省份,城市”),并从中提取城市名。
  2. 手动输入降级:如果自动定位失败(如 API 不可用),程序会提示用户手动输入城市名。
  3. 查询天气信息:将获取到的城市名作为参数,调用api.k780.com的天气 API,获取并解析该城市的天气数据(日期、天气状况、温度、风向、湿度等),最后打印结果。

程序通过封装 HTTP 请求、JSON 解析、字符串处理等功能,实现了从 “IP 定位” 到 “天气查询” 的完整流程,同时包含了错误处理和内存管理,确保运行的稳定性。


网站公告

今日签到

点亮在社区的每一天
去签到