悠悠楠杉
基于C++的天气查询程序开发:网络API调用与JSON解析实战
引言
在现代软件开发中,网络API的调用与数据处理已成为程序员必备的核心技能。本文将详细介绍如何使用C++开发一个功能完整的天气查询程序,涵盖从网络请求到数据解析的全过程。这个项目不仅能帮助读者掌握C++网络编程技巧,还能深入理解RESTful API的工作原理。
开发环境准备
在开始编码前,我们需要搭建合适的开发环境:
cpp
// 必要的库文件
include
include <curl/curl.h> // 网络请求库
include <nlohmann/json.hpp> // JSON解析库
include
include // 用于格式化输出
开发环境配置要点:
1. 安装libcurl开发包(Ubuntu下使用sudo apt-get install libcurl4-openssl-dev
)
2. 添加JSON解析库(推荐使用nlohmann/json,单头文件设计,易于集成)
核心架构设计
1. 网络请求模块
网络请求是程序的基础功能,我们使用libcurl库实现:
cpp
class WeatherFetcher {
private:
static sizet WriteCallback(void* contents, sizet size, sizet nmemb, std::string* output) {
sizet totalsize = size * nmemb;
output->append((char*)contents, totalsize);
return total_size;
}
public:
std::string fetchWeatherData(const std::string& city) {
CURL* curl = curleasyinit();
std::string response;
if (curl) {
std::string api_url = "http://api.weatherapi.com/v1/current.json?key=YOUR_API_KEY&q=" + city;
curl_easy_setopt(curl, CURLOPT_URL, api_url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK) {
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
}
curl_easy_cleanup(curl);
}
return response;
}
};
2. JSON数据解析模块
获取到API响应后,我们需要解析JSON数据:
cpp
class WeatherParser {
public:
void parseAndDisplay(const std::string& jsonData) {
try {
auto json = nlohmann::json::parse(jsonData);
std::cout << "=== 当前天气状况 ===" << std::endl;
std::cout << "城市: " << json["location"]["name"] << std::endl;
std::cout << "区域: " << json["location"]["region"] << std::endl;
std::cout << "国家: " << json["location"]["country"] << std::endl;
std::cout << "当地时间: " << json["location"]["localtime"] << std::endl;
std::cout << "\n=== 气象数据 ===" << std::endl;
std::cout << "温度: " << json["current"]["temp_c"] << "°C / "
<< json["current"]["temp_f"] << "°F" << std::endl;
std::cout << "天气状况: " << json["current"]["condition"]["text"] << std::endl;
std::cout << "风速: " << json["current"]["wind_kph"] << " km/h" << std::endl;
std::cout << "湿度: " << json["current"]["humidity"] << "%" << std::endl;
std::cout << "云量: " << json["current"]["cloud"] << "%" << std::endl;
std::cout << "体感温度: " << json["current"]["feelslike_c"] << "°C" << std::endl;
} catch (const std::exception& e) {
std::cerr << "JSON解析错误: " << e.what() << std::endl;
}
}
};
完整程序实现
将各模块组合起来,形成完整的天气查询程序:
cpp
int main() {
// 初始化libcurl(全局只需一次)
curlglobalinit(CURLGLOBALDEFAULT);
WeatherFetcher fetcher;
WeatherParser parser;
std::string city;
std::cout << "请输入要查询的城市名称: ";
std::getline(std::cin, city);
if (!city.empty()) {
std::string weatherData = fetcher.fetchWeatherData(city);
parser.parseAndDisplay(weatherData);
} else {
std::cout << "城市名称不能为空!" << std::endl;
}
// 清理libcurl资源
curl_global_cleanup();
return 0;
}
进阶功能扩展
1. 错误处理增强
cpp
enum class WeatherError {
NoError,
NetworkError,
InvalidAPIKey,
CityNotFound,
ParseError
};
WeatherError checkForErrors(const std::string& response) {
if (response.empty()) return WeatherError::NetworkError;
try {
auto json = nlohmann::json::parse(response);
if (json.contains("error")) {
if (json["error"]["code"] == 1002) return WeatherError::InvalidAPIKey;
if (json["error"]["code"] == 1006) return WeatherError::CityNotFound;
}
} catch (...) {
return WeatherError::ParseError;
}
return WeatherError::NoError;
}
2. 多城市批量查询
cpp
void batchQuery(const std::vector
WeatherFetcher fetcher;
WeatherParser parser;
for (const auto& city : cities) {
std::cout << "\n查询城市: " << city << std::endl;
std::string weatherData = fetcher.fetchWeatherData(city);
WeatherError err = checkForErrors(weatherData);
if (err != WeatherError::NoError) {
handleWeatherError(err);
continue;
}
parser.parseAndDisplay(weatherData);
}
}
API选择与密钥管理
推荐的天气API服务
- WeatherAPI.com(本文示例使用)
- OpenWeatherMap
- AccuWeather
- 中国气象局API(国内用户)
安全存储API密钥
cpp
class APIConfig {
private:
std::string apiKey;
public:
APIConfig() {
// 从环境变量或配置文件中读取密钥
const char* envKey = std::getenv("WEATHERAPIKEY");
apiKey = envKey ? envKey : "";
if (apiKey.empty()) {
// 尝试从配置文件读取
std::ifstream configFile("config.json");
if (configFile.is_open()) {
try {
nlohmann::json config;
configFile >> config;
apiKey = config["api_key"];
} catch (...) {
// 处理异常
}
}
}
}
std::string getKey() const {
if (apiKey.empty()) {
throw std::runtime_error("API密钥未配置");
}
return apiKey;
}
};
性能优化建议
- 缓存机制:对频繁查询的城市天气数据进行缓存cpp
std::unorderedmap<std::string, std::pair<std::string, timet>> weatherCache;
std::string getCachedWeather(const std::string& city) {
auto it = weatherCache.find(city);
if (it != weatherCache.end() && (time(nullptr) - it->second.second) < 3600) {
return it->second.first; // 1小时内缓存有效
}
return "";
}
- 异步请求:使用多线程处理多个城市查询cpp
include
include
std::vector<std::future
for (const auto& city : cities) {
asyncFetches.push_back(std::async(std::launch::async, &fetcher, city {
return fetcher.fetchWeatherData(city);
}));
}
- 连接池管理:重用CURL句柄提升性能
跨平台兼容性处理
针对不同操作系统的适配方案:
cpp
ifdef _WIN32
// Windows特定初始化
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2), &wsaData);
endif
// 程序退出时
ifdef _WIN32
WSACleanup();
endif
单元测试建议
使用Google Test框架编写测试用例:
cpp
TEST(WeatherParserTest, BasicParsing) {
WeatherParser parser;
std::string testJson = R"({
"location": {
"name": "Beijing",
"country": "China"
},
"current": {
"temp_c": 25.0
}
})";
testing::internal::CaptureStdout();
parser.parseAndDisplay(testJson);
std::string output = testing::internal::GetCapturedStdout();
EXPECT_TRUE(output.find("Beijing") != std::string::npos);
EXPECT_TRUE(output.find("25.0") != std::string::npos);
}
部署与打包
Linux系统打包
- 创建Makefile文件:makefile
CXX = g++
CXXFLAGS = -std=c++11 -Wall
LIBS = -lcurl
weather: main.o
$(CXX) $(CXXFLAGS) -o weather main.o $(LIBS)
main.o: main.cpp
$(CXX) $(CXXFLAGS) -c main.cpp
clean:
rm -f *.o weather
- 打包为deb/rpm包(可选)
Windows系统打包
- 使用Visual Studio创建解决方案
- 配置libcurl依赖
- 生成安装程序(可使用NSIS或Inno Setup)