链接器需要加:-lwinhttp!
链接器需要加:-lwinhttp!!
链接器需要加:-lwinhttp!!!
城市->经纬度部分由AI完成(太多了)
qwq
// 天气查询(Windows / MinGW / RedPanda-CPP)
// 特点:内置全国 305 个地级市坐标表,城市→坐标离线完成,不依赖地理编码接口
// 数据源:高德地图 Web服务(中国气象局实测,需 Key);未填 Key 用 Open-Meteo 兜底
// 链接器需要加:-lwinhttp
#include <windows.h>
#include <winhttp.h>
#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
// ===================================================================
// ★★★ 高德 Key(留空则用 Open-Meteo 兜底,但数值可能差 1~3°C)★★★
// 申请:console.amap.com → 应用管理 → 创建新应用 → 添加Key → 选「Web服务」
// ===================================================================
static const std::string AMAP_KEY = "";
// ===================================================================
// ================= 内置城市坐标表(离线)=================
struct CityInfo { const char* name; const char* adcode; double lat; double lon; };
static const CityInfo CITY_TABLE[] = {
{"北京", "110000", 39.9042, 116.4074},
{"上海", "310000", 31.2304, 121.4737},
{"天津", "120000", 39.3434, 117.3616},
{"重庆", "500000", 29.5630, 106.5516},
{"石家庄", "130100", 38.0428, 114.5149},
{"唐山", "130200", 39.6304, 118.1804},
{"秦皇岛", "130300", 39.9354, 119.6005},
{"邯郸", "130400", 36.6256, 114.5391},
{"邢台", "130500", 37.0682, 114.5049},
{"保定", "130600", 38.8740, 115.4646},
{"张家口", "130700", 40.7688, 114.8865},
{"承德", "130800", 40.9516, 117.9634},
{"沧州", "130900", 38.3037, 116.8388},
{"廊坊", "131000", 39.5379, 116.6835},
{"衡水", "131100", 37.7392, 115.6690},
{"太原", "140100", 37.8706, 112.5489},
{"大同", "140200", 40.0903, 113.3001},
{"阳泉", "140300", 37.8576, 113.5806},
{"长治", "140400", 36.1954, 113.1163},
{"晋城", "140500", 35.4907, 112.8513},
{"朔州", "140600", 39.3315, 112.4329},
{"晋中", "140700", 37.6870, 112.7529},
{"运城", "140800", 35.0263, 111.0038},
{"忻州", "140900", 38.4177, 112.7343},
{"临汾", "141000", 36.0880, 111.5190},
{"吕梁", "141100", 37.5187, 111.1441},
{"呼和浩特", "150100", 40.8414, 111.7519},
{"包头", "150200", 40.6574, 109.8405},
{"乌海", "150300", 39.6549, 106.7955},
{"赤峰", "150400", 42.2580, 118.9560},
{"通辽", "150500", 43.6537, 122.2437},
{"鄂尔多斯", "150600", 39.6084, 109.7805},
{"呼伦贝尔", "150700", 49.2150, 119.7660},
{"巴彦淖尔", "150800", 40.7495, 107.3902},
{"乌兰察布", "150900", 41.0214, 113.1323},
{"沈阳", "210100", 41.8057, 123.4315},
{"大连", "210200", 38.9140, 121.6147},
{"鞍山", "210300", 41.1082, 122.9956},
{"抚顺", "210400", 41.8803, 123.9573},
{"本溪", "210500", 41.2947, 123.7604},
{"丹东", "210600", 40.1245, 124.3924},
{"锦州", "210700", 41.1075, 121.1279},
{"营口", "210800", 40.6658, 122.2344},
{"阜新", "210900", 42.0192, 121.6714},
{"辽阳", "211000", 41.2693, 123.1729},
{"盘锦", "211100", 41.1245, 122.0706},
{"铁岭", "211200", 42.2841, 123.8346},
{"朝阳", "211300", 41.5745, 120.4501},
{"葫芦岛", "211400", 40.7111, 120.8373},
{"长春", "220100", 43.8171, 125.3235},
{"吉林", "220200", 43.8409, 126.5528},
{"四平", "220300", 43.1706, 124.3480},
{"辽源", "220400", 42.8895, 125.1434},
{"通化", "220500", 41.7281, 125.9395},
{"白山", "220600", 41.9410, 126.4200},
{"松原", "220700", 45.1420, 124.8232},
{"白城", "220800", 45.6193, 122.8409},
{"哈尔滨", "230100", 45.8038, 126.5349},
{"齐齐哈尔", "230200", 47.3427, 123.9188},
{"鸡西", "230300", 45.2995, 130.9508},
{"鹤岗", "230400", 47.3326, 130.2834},
{"双鸭山", "230500", 46.6461, 131.1576},
{"大庆", "230600", 46.5883, 125.1100},
{"伊春", "230700", 47.7261, 128.8994},
{"佳木斯", "230800", 46.7969, 130.3177},
{"七台河", "230900", 45.7712, 131.0034},
{"牡丹江", "231000", 44.5856, 129.6206},
{"黑河", "231100", 50.2494, 127.5284},
{"绥化", "231200", 46.6410, 126.9826},
{"南京", "320100", 32.0603, 118.7969},
{"无锡", "320200", 31.4912, 120.3119},
{"徐州", "320300", 34.2618, 117.1588},
{"常州", "320400", 31.8113, 119.9740},
{"苏州", "320500", 31.2989, 120.5853},
{"南通", "320600", 31.9802, 120.8943},
{"连云港", "320700", 34.5967, 119.2216},
{"淮安", "320800", 33.6104, 119.0153},
{"盐城", "320900", 33.3470, 120.1636},
{"扬州", "321000", 32.3931, 119.4215},
{"镇江", "321100", 32.1885, 119.4528},
{"泰州", "321200", 32.4564, 119.9228},
{"宿迁", "321300", 33.9631, 118.2753},
{"杭州", "330100", 30.2741, 120.1551},
{"宁波", "330200", 29.8683, 121.5440},
{"温州", "330300", 27.9949, 120.6994},
{"嘉兴", "330400", 30.7520, 120.7557},
{"湖州", "330500", 30.8939, 120.0877},
{"绍兴", "330600", 30.0303, 120.5804},
{"金华", "330700", 29.0789, 119.6495},
{"衢州", "330800", 28.9416, 118.8737},
{"舟山", "330900", 30.0193, 122.1070},
{"台州", "331000", 28.6614, 121.4207},
{"丽水", "331100", 28.4519, 119.9218},
{"合肥", "340100", 31.8206, 117.2272},
{"芜湖", "340200", 31.3494, 118.3758},
{"蚌埠", "340300", 32.9167, 117.3892},
{"淮南", "340400", 32.6250, 117.0200},
{"马鞍山", "340500", 31.6707, 118.5079},
{"淮北", "340600", 33.9500, 116.7900},
{"铜陵", "340700", 30.9455, 117.8120},
{"安庆", "340800", 30.5089, 117.0435},
{"黄山", "341000", 29.7147, 118.3376},
{"阜阳", "341200", 32.8950, 115.8200},
{"宿州", "341300", 33.6488, 116.9840},
{"滁州", "341100", 32.3033, 118.3163},
{"六安", "341500", 31.7520, 116.5070},
{"亳州", "341600", 33.8720, 115.7800},
{"池州", "341700", 30.6600, 117.4900},
{"宣城", "341800", 30.9455, 118.7580},
{"福州", "350100", 26.0745, 119.2965},
{"厦门", "350200", 24.4798, 118.0894},
{"莆田", "350300", 25.4310, 119.0075},
{"三明", "350400", 26.2654, 117.6218},
{"泉州", "350500", 24.8740, 118.6757},
{"漳州", "350600", 24.5135, 117.6475},
{"南平", "350700", 26.6363, 118.1779},
{"龙岩", "350800", 25.0753, 117.0170},
{"宁德", "350900", 26.6650, 119.5460},
{"南昌", "360100", 28.6820, 115.8579},
{"景德镇", "360200", 29.2925, 117.2084},
{"萍乡", "360300", 27.6222, 113.8532},
{"九江", "360400", 29.7120, 115.9928},
{"新余", "360500", 27.8165, 114.9274},
{"鹰潭", "360600", 28.2380, 117.0700},
{"赣州", "360700", 25.8295, 114.9400},
{"吉安", "360800", 27.1167, 114.9750},
{"宜春", "360900", 27.8114, 114.4056},
{"抚州", "361000", 27.9839, 116.3583},
{"上饶", "361100", 28.4544, 117.9700},
{"济南", "370100", 36.6512, 117.1201},
{"青岛", "370200", 36.0671, 120.3826},
{"淄博", "370300", 36.8135, 118.0550},
{"枣庄", "370400", 34.8100, 117.3200},
{"东营", "370500", 37.4347, 118.6644},
{"烟台", "370600", 37.4638, 121.4479},
{"潍坊", "370700", 36.7069, 119.1617},
{"济宁", "370800", 35.4150, 116.5870},
{"泰安", "370900", 36.2000, 117.0900},
{"威海", "371000", 37.5133, 122.1200},
{"日照", "371100", 35.4200, 119.5300},
{"临沂", "371300", 35.1046, 118.3564},
{"德州", "371400", 37.4341, 116.3570},
{"聊城", "371500", 36.4570, 115.9850},
{"滨州", "371600", 37.3833, 117.9700},
{"菏泽", "371700", 35.2400, 115.4800},
{"郑州", "410100", 34.7466, 113.6254},
{"开封", "410200", 34.7973, 114.3073},
{"洛阳", "410300", 34.6197, 112.4540},
{"平顶山", "410400", 33.7350, 113.1840},
{"安阳", "410500", 36.0990, 114.3980},
{"鹤壁", "410600", 35.7480, 114.2920},
{"新乡", "410700", 35.3020, 113.9260},
{"焦作", "410800", 35.2150, 113.2350},
{"濮阳", "410900", 35.7620, 115.0280},
{"许昌", "411000", 34.0360, 113.8520},
{"漯河", "411100", 33.5850, 114.0210},
{"三门峡", "411200", 34.7770, 111.1940},
{"南阳", "411300", 33.0000, 112.5290},
{"商丘", "411400", 34.4150, 115.6500},
{"信阳", "411500", 32.1470, 114.0920},
{"周口", "411600", 33.6250, 114.6490},
{"驻马店", "411700", 32.9800, 114.0220},
{"武汉", "420100", 30.5928, 114.3055},
{"黄石", "420200", 30.2024, 115.0361},
{"十堰", "420300", 32.6469, 110.7889},
{"宜昌", "420500", 30.6917, 111.2864},
{"襄阳", "420600", 32.0428, 112.1441},
{"鄂州", "420700", 30.5496, 114.8918},
{"荆门", "420800", 30.9884, 112.2040},
{"孝感", "420900", 30.9255, 113.9211},
{"荆州", "421000", 30.3349, 112.2385},
{"黄冈", "421100", 30.4535, 114.8790},
{"咸宁", "421200", 29.8856, 114.3042},
{"随州", "421300", 31.7179, 113.3827},
{"恩施", "422800", 30.2719, 109.4892},
{"长沙", "430100", 28.2282, 112.9388},
{"株洲", "430200", 27.8274, 113.1325},
{"湘潭", "430300", 27.8272, 112.9440},
{"衡阳", "430400", 26.8934, 112.5720},
{"邵阳", "430500", 27.2477, 111.4692},
{"岳阳", "430600", 29.3574, 113.1288},
{"常德", "430700", 29.0320, 111.7030},
{"张家界", "430800", 29.1170, 110.4790},
{"益阳", "430900", 28.5700, 112.3500},
{"郴州", "431000", 25.7740, 113.0200},
{"永州", "431100", 26.4210, 111.6080},
{"怀化", "431200", 27.5680, 110.0000},
{"娄底", "431300", 27.7270, 111.9980},
{"广州", "440100", 23.1291, 113.2644},
{"深圳", "440300", 22.5431, 114.0579},
{"珠海", "440400", 22.2248, 113.5548},
{"汕头", "440500", 23.3541, 116.6820},
{"佛山", "440600", 23.0215, 113.1217},
{"韶关", "440200", 24.8100, 113.5910},
{"湛江", "440800", 21.2710, 110.3590},
{"肇庆", "441200", 23.0470, 112.4650},
{"江门", "440700", 22.5790, 113.0850},
{"茂名", "440900", 21.6630, 110.9250},
{"惠州", "441300", 23.1110, 114.4120},
{"梅州", "441400", 24.2880, 116.1260},
{"汕尾", "441500", 22.7860, 115.3750},
{"河源", "441600", 23.7430, 114.6970},
{"阳江", "441700", 21.8580, 111.9820},
{"清远", "441800", 23.6840, 113.0490},
{"东莞", "441900", 23.0207, 113.7518},
{"中山", "442000", 22.5210, 113.3920},
{"潮州", "445100", 23.6620, 116.6220},
{"揭阳", "445200", 23.5530, 116.3730},
{"云浮", "445300", 22.9150, 112.0450},
{"南宁", "450100", 22.8170, 108.3665},
{"柳州", "450200", 24.3260, 109.4280},
{"桂林", "450300", 25.2736, 110.2900},
{"梧州", "450400", 23.4760, 111.2790},
{"北海", "450500", 21.4730, 109.1200},
{"防城港", "450600", 21.6120, 108.3520},
{"钦州", "450700", 21.9810, 108.6240},
{"贵港", "450800", 23.1110, 109.5990},
{"玉林", "450900", 22.6540, 110.1810},
{"百色", "451000", 23.9010, 106.6180},
{"贺州", "451100", 24.4130, 111.5530},
{"河池", "451200", 24.6940, 108.0850},
{"来宾", "451300", 23.7360, 109.2290},
{"崇左", "451400", 22.4080, 107.3570},
{"海口", "460100", 20.0444, 110.1999},
{"三亚", "460200", 18.2528, 109.5119},
{"儋州", "460400", 19.5210, 109.5800},
{"成都", "510100", 30.5728, 104.0668},
{"自贡", "510300", 29.3390, 104.7780},
{"攀枝花", "510400", 26.5820, 101.7180},
{"泸州", "510500", 28.8720, 105.4420},
{"德阳", "510600", 31.1270, 104.3980},
{"绵阳", "510700", 31.4670, 104.6800},
{"广元", "510800", 32.4350, 105.8430},
{"遂宁", "510900", 30.5330, 105.5900},
{"内江", "511000", 29.5850, 105.0670},
{"乐山", "511100", 29.5520, 103.7660},
{"南充", "511300", 30.8370, 106.1100},
{"眉山", "511400", 30.0490, 103.8480},
{"宜宾", "511500", 28.7510, 104.6280},
{"广安", "511600", 30.4560, 106.6330},
{"达州", "511700", 31.2090, 107.5020},
{"雅安", "511800", 29.9830, 103.0050},
{"巴中", "511900", 31.8580, 106.7480},
{"资阳", "512000", 30.1220, 104.6410},
{"贵阳", "520100", 26.6470, 106.6302},
{"六盘水", "520200", 26.5930, 104.8310},
{"遵义", "520300", 27.7250, 106.9270},
{"安顺", "520400", 26.2530, 105.9460},
{"毕节", "520500", 27.3020, 105.2830},
{"铜仁", "520600", 27.7170, 109.1870},
{"昆明", "530100", 24.8801, 102.8329},
{"曲靖", "530300", 25.5010, 103.7950},
{"玉溪", "530400", 24.3520, 102.5420},
{"保山", "530500", 25.1210, 99.1670},
{"昭通", "530600", 27.3380, 103.7170},
{"丽江", "530700", 26.8550, 100.2330},
{"普洱", "530800", 22.7850, 100.9720},
{"临沧", "530900", 23.8870, 100.0890},
{"拉萨", "540100", 29.6520, 91.1721},
{"日喀则", "540200", 29.2670, 88.8790},
{"昌都", "540300", 31.1360, 97.1780},
{"林芝", "540400", 29.6490, 94.3620},
{"西安", "610100", 34.3416, 108.9398},
{"铜川", "610200", 34.8960, 108.9450},
{"宝鸡", "610300", 34.3620, 107.2370},
{"咸阳", "610400", 34.3300, 108.7100},
{"渭南", "610500", 34.5000, 109.5030},
{"延安", "610600", 36.5960, 109.4890},
{"汉中", "610700", 33.0670, 107.0230},
{"榆林", "610800", 38.2840, 109.7350},
{"安康", "610900", 32.6850, 109.0290},
{"商洛", "611000", 33.8680, 110.1010},
{"兰州", "620100", 36.0611, 103.8343},
{"嘉峪关", "620200", 39.7720, 98.2890},
{"金昌", "620300", 38.5200, 102.1880},
{"白银", "620400", 36.5450, 104.1780},
{"天水", "620500", 34.5810, 105.7250},
{"武威", "620600", 37.9280, 102.6380},
{"张掖", "620700", 38.9260, 100.4510},
{"平凉", "620800", 35.5400, 106.6850},
{"酒泉", "620900", 39.7330, 98.5080},
{"庆阳", "621000", 35.7090, 107.6390},
{"定西", "621100", 35.5810, 104.6260},
{"陇南", "621200", 33.4000, 104.9200},
{"西宁", "630100", 36.6171, 101.7782},
{"海东", "630200", 36.5030, 102.1040},
{"银川", "640100", 38.4872, 106.2309},
{"石嘴山", "640200", 39.0170, 106.3750},
{"吴忠", "640300", 37.9930, 106.1940},
{"固原", "640400", 36.0050, 106.2780},
{"中卫", "640500", 37.5140, 105.1890},
{"乌鲁木齐", "650100", 43.8256, 87.6168},
{"克拉玛依", "650200", 45.5960, 84.8730},
{"吐鲁番", "650400", 42.9530, 89.1890},
{"哈密", "650500", 42.8270, 93.5150},
{"昌吉", "652300", 44.0120, 87.3080},
{"库尔勒", "652800", 41.7260, 86.1740},
{"阿克苏", "652900", 41.1710, 80.2640},
{"喀什", "653100", 39.4700, 75.9890},
{"伊宁", "654000", 43.9160, 81.3240},
{"塔城", "654200", 46.7460, 82.9810},
{"阿勒泰", "654300", 47.8480, 88.1400},
{"香港", "810000", 22.3193, 114.1694},
{"澳门", "820000", 22.1987, 113.5439},
{"台北", "710000", 25.0330, 121.5654},
};
static const int CITY_COUNT = (int)(sizeof(CITY_TABLE) / sizeof(CITY_TABLE[0]));
// 去掉尾部「市」「县」「区」「自治州」等行政后缀,便于匹配
static std::string StripSuffix(const std::string& s) {
static const char* suf[] = { "自治州", "地区", "市", "县", "区", "省" };
std::string r = s;
for (int i = 0; i < 6; ++i) {
size_t len = strlen(suf[i]);
if (r.size() > len && r.compare(r.size() – len, len, suf[i]) == 0)
return r.substr(0, r.size() – len);
}
return r;
}
// UTF-8 字符数(中文一个字算 1,不能用字节数判断)
static size_t Utf8Len(const std::string& s) {
size_t n = 0;
for (unsigned char c : s) if ((c & 0xC0) != 0x80) ++n;
return n;
}
// 精确优先,其次去后缀,最后子串包含
static const CityInfo* FindCity(const std::string& q) {
if (q.empty()) return NULL;
for (int i = 0; i < CITY_COUNT; ++i)
if (q == CITY_TABLE[i].name) return &CITY_TABLE[i];
std::string bare = StripSuffix(q);
for (int i = 0; i < CITY_COUNT; ++i)
if (bare == CITY_TABLE[i].name) return &CITY_TABLE[i];
if (Utf8Len(bare) < 2) return NULL;
for (int i = 0; i < CITY_COUNT; ++i)
if (std::string(CITY_TABLE[i].name).find(bare) != std::string::npos)
return &CITY_TABLE[i];
return NULL;
}
// ================= 编码 / 网络 =================
static std::string UrlEncode(const std::string& in) {
static const char* hex = "0123456789ABCDEF";
std::string out;
for (unsigned char c : in) {
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') out += (char)c;
else { out += '%'; out += hex[c >> 4]; out += hex[c & 15]; }
}
return out;
}
static std::wstring ToWide(const std::string& utf8) {
if (utf8.empty()) return L"";
int n = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), (int)utf8.size(), NULL, 0);
std::wstring w(n, L'\\0');
MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), (int)utf8.size(), &w[0], n);
return w;
}
static bool LooksLikeGzip(const std::string& s) {
return s.size() >= 2 && (unsigned char)s[0] == 0x1F && (unsigned char)s[1] == 0x8B;
}
// 同步 HTTP(S) GET;status 返回 HTTP 状态码(0 表示请求本身失败)
static std::string HttpGet(const std::wstring& host, const std::string& path, bool https,
DWORD* status = NULL, bool allowGzip = false) {
std::string out;
if (status) *status = 0;
HINTERNET hSession = WinHttpOpen(L"WeatherDemo/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession) return out;
DWORD policy = WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS;
WinHttpSetOption(hSession, WINHTTP_OPTION_REDIRECT_POLICY, &policy, sizeof(policy));
HINTERNET hConn = WinHttpConnect(hSession, host.c_str(),
https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT, 0);
if (!hConn) { WinHttpCloseHandle(hSession); return out; }
std::wstring wpath = ToWide(path);
HINTERNET hReq = WinHttpOpenRequest(hConn, L"GET", wpath.c_str(), NULL,
WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, https ? WINHTTP_FLAG_SECURE : 0);
if (!hReq) { WinHttpCloseHandle(hConn); WinHttpCloseHandle(hSession); return out; }
// 仅在二次重试时开启压缩(首次不带,兼容老系统)
if (allowGzip) {
#ifndef WINHTTP_OPTION_DECOMPRESSION
#define WINHTTP_OPTION_DECOMPRESSION 118
#endif
DWORD mask = 0x00000001 | 0x00000002; // GZIP | DEFLATE
WinHttpSetOption(hReq, WINHTTP_OPTION_DECOMPRESSION, &mask, sizeof(mask));
}
WinHttpSetTimeouts(hSession, 10000, 10000, 10000, 10000);
if (WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) &&
WinHttpReceiveResponse(hReq, NULL)) {
if (status) {
DWORD code = 0, len = sizeof(code);
if (WinHttpQueryHeaders(hReq,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX, &code, &len, WINHTTP_NO_HEADER_INDEX))
*status = code;
}
DWORD avail = 0, read = 0;
while (WinHttpQueryDataAvailable(hReq, &avail) && avail > 0) {
std::string buf(avail, '\\0');
if (!WinHttpReadData(hReq, &buf[0], avail, &read) || read == 0) break;
out.append(buf, 0, read);
}
}
WinHttpCloseHandle(hReq);
WinHttpCloseHandle(hConn);
WinHttpCloseHandle(hSession);
return out;
}
// 带诊断的请求:空响应 / gzip / 非 JSON 都会明确提示
static std::string FetchJSON(const std::wstring& host, const std::string& path, bool https) {
DWORD st = 0;
std::string j = HttpGet(host, path, https, &st, false);
if (LooksLikeGzip(j)) // 服务器强制压缩,重开一次带解压
j = HttpGet(host, path, https, &st, true);
if (j.empty()) {
std::wstring h(host.begin(), host.end());
std::wcout << L"(请求失败:" << h << L",HTTP=" << st
<< L",WinError=" << GetLastError() << L")\\n";
return "";
}
if (LooksLikeGzip(j)) {
std::cout << "(响应仍是压缩数据,系统不支持自动解压,请升级到 Win10 1803+)\\n";
return "";
}
if (j[0] != '{') {
std::cout << "(接口返回非 JSON,状态码=" << st << ",前 200 字节:)\\n"
<< j.substr(0, 200) << "\\n";
return "";
}
return j;
}
// ================= JSON 解析 =================
// \\uXXXX → UTF-8;本来就是中文则原样返回
static std::string DecodeUnicode(const std::string& s) {
std::string out; bool changed = false;
for (size_t i = 0; i < s.size(); ) {
if (s[i] == '\\\\' && i + 5 < s.size() && s[i + 1] == 'u') {
std::string hex = s.substr(i + 2, 4);
if (hex.find_first_not_of("0123456789abcdefABCDEF") == std::string::npos) {
unsigned cp = (unsigned)strtoul(hex.c_str(), NULL, 16);
if (cp < 0x80) {
out += (char)cp;
} else if (cp < 0x800) {
out += (char)(0xC0 | (cp >> 6));
out += (char)(0x80 | (cp & 0x3F));
} else {
out += (char)(0xE0 | (cp >> 12));
out += (char)(0x80 | ((cp >> 6) & 0x3F));
out += (char)(0x80 | (cp & 0x3F));
}
changed = true; i += 6; continue;
}
}
out += s[i++];
}
if (!changed) { for (unsigned char c : s) if (c >= 0x80) return s; }
return out;
}
static bool jsonStr(const std::string& s, const std::string& key, std::string& val) {
std::string k = "\\"" + key + "\\"";
size_t p = s.find(k);
if (p == std::string::npos) return false;
if ((p = s.find(':', p + k.size())) == std::string::npos) return false;
if ((p = s.find('"', p)) == std::string::npos) return false;
size_t e = s.find('"', ++p);
if (e == std::string::npos) return false;
val = DecodeUnicode(s.substr(p, e – p));
return true;
}
static bool jsonNum(const std::string& s, const std::string& key, double& val) {
std::string k = "\\"" + key + "\\"";
size_t p = s.find(k);
if (p == std::string::npos) return false;
if ((p = s.find(':', p + k.size())) == std::string::npos) return false;
// 跳过空白,兼容 "key":[12.3] 数组形式(取第一个元素)
while (++p < s.size() && (s[p] == ' ' || s[p] == '\\t' || s[p] == '\\n' || s[p] == '\\r'
|| s[p] == '[' || s[p] == ',')) {}
char* end = nullptr;
double v = strtod(s.c_str() + p, &end);
if (end == s.c_str() + p) return false;
val = v; return true;
}
// 截取 key 对应对象(大括号配平,避开 current_units 之类的同名前缀)
static std::string jsonObj(const std::string& s, const std::string& key) {
std::string k = "\\"" + key + "\\":";
size_t p = s.find(k);
if (p == std::string::npos) return "";
if ((p = s.find('{', p + k.size())) == std::string::npos) return "";
int depth = 0;
for (size_t i = p; i < s.size(); ++i) {
if (s[i] == '{') ++depth;
else if (s[i] == '}') { if (–depth == 0) return s.substr(p, i – p + 1); }
}
return "";
}
// 截取 key 对应数组里的每个扁平对象
static std::vector<std::string> jsonArrayObjs(const std::string& s, const std::string& key) {
std::vector<std::string> res;
std::string k = "\\"" + key + "\\":";
size_t p = s.find(k);
if (p == std::string::npos) return res;
p = s.find('[', p + k.size());
if (p == std::string::npos) return res;
int depth = 0; size_t start = std::string::npos;
for (size_t i = p; i < s.size(); ++i) {
char c = s[i];
if (c == '[') ++depth;
else if (c == ']') { if (–depth == 0) break; }
else if (c == '{' && depth == 1) start = i;
else if (c == '}' && depth == 1 && start != std::string::npos) {
res.push_back(s.substr(start, i – start + 1));
start = std::string::npos;
}
}
return res;
}
// ================= WMO 天气代码 → 中文(仅 Open-Meteo 路径)=================
static const char* WeatherDesc(int code) {
switch (code) {
case 0: return "晴";
case 1: return "大部晴朗";
case 2: return "局部多云";
case 3: return "阴";
case 45: case 48: return "雾";
case 51: case 53: case 55: return "毛毛雨";
case 56: case 57: return "冻雨";
case 61: return "小雨";
case 63: return "中雨";
case 65: return "大雨";
case 66: case 67: return "冻雨";
case 71: case 73: case 75: return "雪";
case 77: return "米雪";
case 80: case 81: case 82: return "阵雨";
case 85: case 86: return "阵雪";
case 95: return "雷阵雨";
case 96: case 99: return "雷暴伴冰雹";
default: return "未知";
}
}
// ================= IP 定位 =================
static bool CityByIP_Amap(std::string& city, std::string& adcode, std::string& province) {
std::string j = FetchJSON(L"restapi.amap.com", "/v3/ip?key=" + AMAP_KEY, true);
if (j.empty()) return false;
std::string st;
jsonStr(j, "status", st);
if (st != "1") return false;
jsonStr(j, "province", province);
jsonStr(j, "city", city);
jsonStr(j, "adcode", adcode);
return !city.empty();
}
static bool CityByIP_Free(std::string& city) {
std::string j = FetchJSON(L"ip-api.com", "/json/?lang=zh-CN&fields=status,city", false);
return !j.empty() && jsonStr(j, "city", city) && !city.empty();
}
// ================= 天气:高德(中国气象局实测)=================
static bool GetWeatherAmap(const std::string& query) {
std::string url = "/v3/weather/weatherInfo?city=" + UrlEncode(query)
+ "&key=" + AMAP_KEY + "&extensions=base&output=JSON";
std::string j = FetchJSON(L"restapi.amap.com", url, true);
if (j.empty()) return false;
std::string st, info;
jsonStr(j, "status", st);
if (st != "1") {
jsonStr(j, "info", info);
std::cout << "(高德返回:" << (info.empty() ? st : info) << ")\\n";
return false;
}
std::vector<std::string> arr = jsonArrayObjs(j, "lives");
if (arr.empty()) { std::cout << "(高德解析失败:\\n" << j << ")\\n"; return false; }
std::string w, t, h, wd, wp, rt, pc, cc;
jsonStr(arr[0], "province", pc);
jsonStr(arr[0], "city", cc);
jsonStr(arr[0], "weather", w);
jsonStr(arr[0], "temperature", t);
jsonStr(arr[0], "humidity", h);
jsonStr(arr[0], "winddirection", wd);
jsonStr(arr[0], "windpower", wp);
jsonStr(arr[0], "reporttime", rt);
std::string loc = cc.empty() ? query : cc;
if (!pc.empty() && loc.find(pc) == std::string::npos) loc = pc + " " + loc;
std::cout << "\\n===== " << loc << " 实时天气 =====\\n";
std::cout << "天气:" << w << "\\n";
std::cout << "温度:" << t << " °C\\n";
std::cout << "湿度:" << h << " %\\n";
std::cout << "风力:" << wd << "风 " << wp << " 级\\n";
std::cout << "发布时间:" << rt << "\\n";
std::cout << "(数据源:中国气象局 / 高德)\\n";
return true;
}
// ================= 天气:Open-Meteo(兜底,数值预报)=================
static bool GetWeatherOpenMeteo(double lat, double lon, const std::string& label) {
char url[512];
snprintf(url, sizeof(url),
"/v1/forecast?latitude=%.4f&longitude=%.4f"
"¤t=temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m"
"&daily=temperature_2m_max,temperature_2m_min"
"&timezone=auto&forecast_days=1", lat, lon);
std::string j = FetchJSON(L"api.open-meteo.com", url, true);
if (j.empty()) return false;
// 必须先切出 current 对象,否则会误取 current_units 里的单位字符串
std::string cur = jsonObj(j, "current");
if (cur.empty()) { std::cout << "(解析失败,原始返回:\\n" << j << ")\\n"; return false; }
double temp = 0, humi = 0, feel = 0, wcode = 0, wind = 0;
jsonNum(cur, "temperature_2m", temp);
jsonNum(cur, "relative_humidity_2m", humi);
jsonNum(cur, "apparent_temperature", feel);
jsonNum(cur, "weather_code", wcode);
jsonNum(cur, "wind_speed_10m", wind);
std::string day = jsonObj(j, "daily");
double tmax = 0, tmin = 0;
if (!day.empty()) {
jsonNum(day, "temperature_2m_max", tmax);
jsonNum(day, "temperature_2m_min", tmin);
}
std::cout << "\\n===== " << label << " 实时天气 =====\\n";
std::cout << "坐标:" << lat << ", " << lon << "\\n";
std::cout << "天气:" << WeatherDesc((int)wcode) << "\\n";
std::cout << "温度:" << temp << " °C(体感 " << feel << " °C)\\n";
if (tmax != 0 || tmin != 0)
std::cout << "今日:" << tmin << " ~ " << tmax << " °C\\n";
std::cout << "湿度:" << humi << " %\\n";
std::cout << "风速:" << wind << " km/h\\n";
std::cout << "(数据源:Open-Meteo 数值预报,与站点实测可能有 1~3°C 差异)\\n";
return true;
}
// ================= 主流程 =================
static void PrintNotFound(const std::string& q) {
std::cout << "\\n内置城市表未收录「" << q << "」。\\n";
std::cout << "可以:\\n";
std::cout << " 1) 换用地级市名(如「随州」而不是「随县」)\\n";
std::cout << " 2) 直接输入经纬度,格式:纬度,经度 例如 31.7179,113.3827\\n";
std::cout << " 3) 填高德 Key 后可查任意区县(高德按名称查询,不受内置表限制)\\n";
}
int main() {
//SetConsoleOutputCP(CP_UTF8);
const bool hasAmap = !AMAP_KEY.empty();
std::string city;
std::cout << "输入城市名(留空=按IP识别,也可直接输 纬度,经度):";
std::getline(std::cin, city);
// 去掉首尾空白
while (!city.empty() && (unsigned char)city[0] <= ' ') city.erase(0, 1);
while (!city.empty() && (unsigned char)city[city.size() – 1] <= ' ') city.erase(city.size() – 1);
if (city.empty()) {
std::string adcode, province;
std::cout << "正在按 IP 识别所在地区…\\n";
bool got = hasAmap ? CityByIP_Amap(city, adcode, province) : CityByIP_Free(city);
if (!got && hasAmap) { got = CityByIP_Free(city); adcode.clear(); }
if (got && !city.empty()) {
std::cout << "IP 定位到「" << (province.empty() ? city : province + " " + city) << "」\\n";
std::cout << "IP 定位只到城市级,可能偏到运营商节点城市;"
"回车接受,或输入正确城市名纠正\\n> ";
std::string fix;
std::getline(std::cin, fix);
if (!fix.empty()) { city = fix; adcode.clear(); province.clear(); }
}
if (city.empty()) {
std::cerr << "IP 识别失败,请手动输入城市名\\n";
system("pause"); return 1;
}
}
// 直接输入经纬度:31.7179,113.3827
double lat = 0, lon = 0;
if (sscanf(city.c_str(), "%lf,%lf", &lat, &lon) == 2 ||
sscanf(city.c_str(), "%lf , %lf", &lat, &lon) == 2) {
bool ok = false;
if (hasAmap) {
// 高德要求「经度,纬度」,与 Open-Meteo 的「纬度,经度」顺序相反
char amapXY[64];
snprintf(amapXY, sizeof(amapXY), "%.6f,%.6f", lon, lat);
ok = GetWeatherAmap(amapXY);
}
if (!ok) ok = GetWeatherOpenMeteo(lat, lon, city);
if (!ok) { std::cerr << "查询失败\\n"; system("pause"); return 1; }
system("pause"); return 0;
}
// 内置表查询(离线,零依赖)
const CityInfo* ci = FindCity(city);
if (!ci) {
// 有 Key 时仍可交给高德按名称查
if (hasAmap && GetWeatherAmap(city)) { system("pause"); return 0; }
std::cout << "(" << (hasAmap ? "高德按名称查询也失败" : "未填高德Key,无法在线查询") << ")\\n";
PrintNotFound(city);
system("pause"); return 1;
}
std::cout << "匹配:" << ci->name << "(adcode " << ci->adcode
<< "," << ci->lat << ", " << ci->lon << ")\\n";
bool ok = false;
if (hasAmap) {
ok = GetWeatherAmap(ci->adcode); // adcode 最精确
if (!ok) { std::cout << "adcode 查询失败,改用城市名重试…\\n"; ok = GetWeatherAmap(ci->name); }
if (!ok) { std::cout << "高德不可用,自动回退 Open-Meteo…\\n"; }
}
if (!ok) ok = GetWeatherOpenMeteo(ci->lat, ci->lon, ci->name);
if (!ok) {
std::cerr << "查询失败:天气接口不可达。可填高德 Key 换用国内数据源。\\n";
system("pause"); return 1;
}
system("pause");
return 0;
}
网硕互联帮助中心



评论前必须登录!
注册