4.完成html文件读取|获取title|获取content|构建url|调试(C++)

解析html代码结构编写
  1. 建立一个util.hpp,是一个工具集,把所有的工具内容写到这里
touch util.hpp

![[Pasted image 20250215140053.png]]

  1. 编写util.hpp
#include <iostream>
#include <string>
#include <fstream>

namespace ns_util{
	class FileUtil{
		public:
			static bool ReadFile(const std::string &file_path, std::string *out)
			{
				return true;
			}
	};
}   

提取title
![[Pasted image 20250215141021.png]]

提取content,本质是进行去标签
3. 编写parser.cc第二步的代码结构

static bool ParseTiltle(const std::string &file, std::string *title)
{
	return true;
}

static bool ParseContent(const std::string &file, std::string *content)
{
	return true;
}

static bool ParseUrl()
{
	return true;
}

bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo_t> *results)
{
	for(const std::string &file : files_list){
		//读取文件,Read();
		std::string result;
		if(!ns_util::FileUtil::ReadFile(file, &result)){
			continue;
		}
		DocInfo_t doc;
		//解析指定的文件,提取title
		if(!ParseTitle(result, &doc.title)){
			continue;
		}
		//解析指定的文件,提取content,就是去标签
		if(!ParseContent(result, &doc.content)){
			continue;
		}
		//解析指定的文件路径,构建url
		if(!ParseUrl()){
			continue;
		}

		//done,一定是完成了解析任务,当前文档的所有的相关结果都保存在了doc里面
		results->push_back(doc); 
		//bug:todo;细节,本质会发生拷贝,效率可能会比较低
	}
	return true;
}
编写文件读取代码
  1. 编写util文件
#include <iostream>
#include <string>
#include <fstream>

namespace ns_util{
	class FileUtil{
		public:
			static bool ReadFile(const std::string &file_path, std::string *out)
			{
				std::ifstream in(file_path, std::ios::in);
				if(!in.is_open()){
					std::cerr << "open file" << file_path << " error" << std::endl;
					return false;
				}

				std::string line;
				while(std::getline(in, line)){ //如何理解getline读取到文件结束:getline的返回值是一个&,while判断的是一个bool类型,本质是因为返回的对象当中重载了强制类型转化
					*out += line;
				}

				in.close();
				return true;
			}
	};
}
编写获取title代码

在整个文档里面去搜索title关键字和/title关键字
![[Pasted image 20250215152947.png]]

找到title关键字的开始位置,和/title关键字的开始位置
让头位置+上title的大小,就是有效区的起始,后面的查找到的位置,这是一个前闭后开的区间

static bool ParseTitle(const std::string &file, std::string *title)
{
	std::size_t begin = file.find("<title>");
	if(begin == std::string::npos){
		return false;
	}
	std::size_t end = file.find("</title>");
	if(end == std::string::npos){
		return false;
	}
	begin += std::string("<title>").size();
	
	if(begin > end){
		return false;
	}

	*title = file.substr(begin, end - begin);
	return true;
}
获取文档的content内容

在进行遍历的时候,只要碰到了>右标签,就意味着当前的标签被处理完毕
只要碰到了<左标签,就意味着新的标签开始了

static bool ParseContent(const std::string &file, std::string *content)
{
	//去标签,基于一个简易的状态机
	enum status{
		LABLE,
		CONTENT
	};

	enum status s = LABLE;
	for(char c : file){
		switch(s){
			case LABLE:
				if(c == '>')
					s = CONTENT;
				break;
			case CONTENT:
				if(c == '<')
					s = LABLE;
				else{
					//不想保留原始文件中的\n,因为想用\n作为html解析之后文本的分隔符
					if(c == '\n')
							c = ' ';
					content->push_back(c);
				}
				break;
			default:
				break;
		}
	}
	return true;
}
编写构建url代码

boost库的官方文档,和下载下来的文档是有路径的对应关系的

官⽹URL样例:
https://www.boost.org/doc/libs/1_78_0/doc/html/accumulators.html  

我们下载下来的url样例:
boost_1_78_0/doc/html/accumulators.html  

我们拷⻉到我们项⽬中的样例:
data/input/accumulators.html 
//我们把下载下来的boost库  doc/html/* copy data/input/

url_head = "https://www.boost.org/doc/libs/1_78_0/doc/html";  
url_tail = [data/input](删除) /accumulators.html -> url_tail =  /accumulators.html  
url = url_head + url_tail ; 相当于形成了⼀个官⽹链接

编写parser.cc

static bool ParseUrl(const std::string &file_path, std::string *url)
{
	std::string url_head = "https://www.boost.org/doc/libs/1_87_0/doc/html";
	std::string url_tail = file_path.substr(src_path.size());
	
	*url = url_head + url_tail;
	return true;
}

bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo_t> *results)
{
	for(const std::string &file : files_list){
		//读取文件,Read();
		std::string result;
		if(!ns_util::FileUtil::ReadFile(file, &result)){
			continue;
		}
		DocInfo_t doc;
		//解析指定的文件,提取title
		if(!ParseTitle(result, &doc.title)){
			continue;
		}
		//解析指定的文件,提取content,就是去标签
		if(!ParseContent(result, &doc.content)){
			continue;
		}
		//解析指定的文件路径,构建url
		if(!ParseUrl(file, &doc.url)){
			continue;
		}
		//done,一定是完成了解析任务,当前文档的所有的相关结果都保存在了doc里面
		results->push_back(doc); //bug:todo;细节,本质会发生拷贝,效率可能会比较低
	}
	return true;
}
调试
void ShowDoc(const DocInfo_t &doc)
{
	std::cout << "title: " << doc.title << std::endl;
	std::cout << "content: " << doc.content << std::endl;
	std::cout << "url: " << doc.url << std::endl;
}

make链接,运行文件
![[Pasted image 20250215171014.png]]

Chapter 46. Boost.YAP - 1.87.0
![[Pasted image 20250215171623.png]]

成功获取到官方url

#include <WiFi.h> #include <WebServer.h> #include <Preferences.h> // 全局变量和对象 WebServer server(80); Preferences preferences; // WiFi配置结构体 struct WiFiConfig { String ssid; String password; }; // 全局WiFi配置 WiFiConfig wifiConfig; bool wifiConnected = false; bool shouldRestart = false; bool webServerStarted = false; // ==================== 信号量和互斥锁 ==================== SemaphoreHandle_t wifiSemaphore; SemaphoreHandle_t webSemaphore; // ==================== WiFi管理任务 ==================== void wifiTask(void *parameter) { Serial.println("WiFi任务启动"); for(;;) { if (xSemaphoreTake(wifiSemaphore, portMAX_DELAY) == pdTRUE) { if (!wifiConnected) { // 尝试连接WiFi Serial.print("尝试连接到WiFi: "); Serial.println(wifiConfig.ssid); if (wifiConfig.ssid.length() > 0) { WiFi.disconnect(true); delay(1000); WiFi.begin(wifiConfig.ssid.c_str(), wifiConfig.password.c_str()); int attempts = 0; while (WiFi.status() != WL_CONNECTED && attempts < 15) { delay(1000); Serial.print("."); attempts++; } if (WiFi.status() == WL_CONNECTED) { wifiConnected = true; Serial.println("\nWiFi连接成功!"); Serial.print("IP地址: "); Serial.println(WiFi.localIP()); // 确保Web服务器在正确的IP上运行 if (!webServerStarted) { server.begin(); webServerStarted = true; Serial.println("Web服务器已启动"); } } else { Serial.println("\nWiFi连接失败!"); // 启动AP模式作为备用 if (!WiFi.softAPgetStationNum()) { WiFi.softAP("ESP32-S3-Config", "12345678"); Serial.print("AP模式启动,IP: "); Serial.println(WiFi.softAPIP()); } } } else { // 没有配置,启动AP模式 if (!WiFi.softAPgetStationNum()) { WiFi.softAP("ESP32-S3-Config", "12345678"); Serial.print("AP模式启动,IP: "); Serial.println(WiFi.softAPIP()); } } } else { // 维持WiFi连接 if (WiFi.status() != WL_CONNECTED) { wifiConnected = false; Serial.println("WiFi连接丢失,尝试重新连接..."); } } xSemaphoreGive(wifiSemaphore); } // 检查是否需要重启 if (shouldRestart) { Serial.println("准备重启..."); delay(2000); ESP.restart(); } delay(2000); // 任务延时 } } // ==================== 辅助函数:创建密码显示字符串 ==================== String getPasswordDisplay() { if (wifiConfig.password.length() > 0) { return "***"; } else { return "无"; } } // ==================== Web服务器路由处理 ==================== void handleRoot() { if (xSemaphoreTake(webSemaphore, pdMS_TO_TICKS(5000)) == pdTRUE) { String html = "<!DOCTYPE html>"; html += "<html>"; html += "<head>"; html += "<title>ESP32-S3 WiFi配置</title>"; html += "<meta charset=\"UTF-8\">"; html += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"; html += "<style>"; html += "body { font-family: Arial, sans-serif; max-width: 500px; margin: 50px auto; padding: 20px; background: #f5f5f5; }"; html += ".container { background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }"; html += "h1 { color: #2c3e50; text-align: center; margin-bottom: 30px; }"; html += ".form-group { margin-bottom: 20px; }"; html += "label { display: block; margin-bottom: 5px; font-weight: bold; color: #34495e; }"; html += "input[type=\"text\"], input[type=\"password\"] { width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 5px; box-sizing: border-box; font-size: 16px; }"; html += "button { background: #3498db; color: white; border: none; padding: 12px 20px; border-radius: 5px; cursor: pointer; width: 100%; font-size: 16px; margin-top: 10px; }"; html += "button:hover { background: #2980b9; }"; html += ".status { padding: 15px; border-radius: 5px; margin: 20px 0; text-align: center; }"; html += ".connected { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }"; html += ".disconnected { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }"; html += ".info { background: #d1ecf1; color: #0c5460; border: 1px solid #bee5eb; margin-top: 20px; padding: 15px; border-radius: 5px; }"; html += "</style>"; html += "</head>"; html += "<body>"; html += "<div class=\"container\">"; html += "<h1>📶 ESP32-S3 WiFi配置</h1>"; html += "<div class=\"info\">"; html += "<strong>当前状态:</strong> "; html += "<span id=\"statusText\">" + String(wifiConnected ? "已连接" : "未连接") + "</span>"; html += "<br><strong>IP地址:</strong> <span id=\"ipText\">" + (wifiConnected ? WiFi.localIP().toString() : WiFi.softAPIP().toString()) + "</span>"; html += "</div>"; html += "<form action=\"/config\" method=\"POST\">"; html += "<div class=\"form-group\">"; html += "<label for=\"ssid\">WiFi名称 (SSID):</label>"; html += "<input type=\"text\" id=\"ssid\" name=\"ssid\" value=\"" + wifiConfig.ssid + "\" required>"; html += "</div>"; html += "<div class=\"form-group\">"; html += "<label for=\"password\">WiFi密码:</label>"; html += "<input type=\"password\" id=\"password\" name=\"password\" value=\"" + wifiConfig.password + "\" required>"; html += "</div>"; html += "<button type=\"submit\">保存并连接</button>"; html += "</form>"; html += "<div class=\"info\">"; html += "<strong>说明:</strong><br>"; html += "1. 填写您的WiFi名称和密码<br>"; html += "2. 点击保存后设备将自动重启并尝试连接<br>"; html += "3. 连接成功后可通过显示的IP地址访问设备"; html += "</div>"; html += "</div>"; html += "<script>"; html += "function updateStatus() {"; html += "fetch('/status').then(response => response.json()).then(data => {"; html += "document.getElementById('statusText').textContent = data.connected ? '已连接' : '未连接';"; html += "document.getElementById('ipText').textContent = data.ip;"; html += "}).catch(err => console.log('状态更新失败:', err));"; html += "}"; html += "setInterval(updateStatus, 5000);"; html += "</script>"; html += "</body>"; html += "</html>"; server.send(200, "text/html", html); xSemaphoreGive(webSemaphore); } else { server.send(500, "text/plain", "服务器繁忙,请稍后重试"); } } void handleConfig() { if (xSemaphoreTake(wifiSemaphore, pdMS_TO_TICKS(5000)) == pdTRUE) { // 获取表单数据 wifiConfig.ssid = server.arg("ssid"); wifiConfig.password = server.arg("password"); // 保存到Preferences preferences.putString("ssid", wifiConfig.ssid); preferences.putString("password", wifiConfig.password); Serial.println("新的WiFi配置已保存:"); Serial.print("SSID: "); Serial.println(wifiConfig.ssid); Serial.print("Password: "); Serial.println(getPasswordDisplay()); // 发送响应 String html = "<!DOCTYPE html>"; html += "<html>"; html += "<head>"; html += "<title>配置已保存</title>"; html += "<meta charset=\"UTF-8\">"; html += "<style>"; html += "body { font-family: Arial; text-align: center; margin: 100px; background: #f5f5f5; }"; html += ".message { background: white; padding: 40px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }"; html += ".success { color: #27ae60; font-size: 24px; margin-bottom: 20px; }"; html += "</style>"; html += "<meta http-equiv=\"refresh\" content=\"5;url=/\">"; html += "</head>"; html += "<body>"; html += "<div class=\"message\">"; html += "<div class=\"success\">✅ 配置已保存!</div>"; html += "<p>设备正在重启并尝试连接WiFi...</p>"; html += "<p>5秒后自动返回</p>"; html += "</div>"; html += "</body>"; html += "</html>"; server.send(200, "text/html", html); xSemaphoreGive(wifiSemaphore); // 标记需要重启 shouldRestart = true; } else { server.send(500, "text/plain", "配置保存失败,请稍后重试"); } } void handleStatus() { String json = "{"; json += "\"connected\":" + String(wifiConnected ? "true" : "false"); json += ",\"ip\":\"" + (wifiConnected ? WiFi.localIP().toString() : WiFi.softAPIP().toString()) + "\""; json += "}"; server.send(200, "application/json", json); } void handleNotFound() { server.send(404, "text/plain", "页面未找到"); } // ==================== Web服务器任务 ==================== void webServerTask(void *parameter) { Serial.println("Web服务器任务启动"); // 设置Web服务器路由 server.on("/", HTTP_GET, handleRoot); server.on("/config", HTTP_POST, handleConfig); server.on("/status", HTTP_GET, handleStatus); server.onNotFound(handleNotFound); // 等待WiFi初始化完成 delay(3000); // 启动Web服务器(在AP模式下) server.begin(); webServerStarted = true; Serial.println("Web服务器已启动"); // Web服务器处理循环 for(;;) { server.handleClient(); delay(10); // 增加延迟以减少CPU负载 } } // ==================== 初始化设置 ==================== void setup() { Serial.begin(115200); delay(1000); Serial.println("\n🚀 ESP32-S3 WiFi配置服务器启动"); Serial.println("=================================="); // 初始化信号量 wifiSemaphore = xSemaphoreCreateMutex(); webSemaphore = xSemaphoreCreateMutex(); if (wifiSemaphore == NULL || webSemaphore == NULL) { Serial.println("信号量创建失败!"); while(1) delay(1000); } // 初始化Preferences preferences.begin("wifi-config", false); // 读取保存的WiFi配置 wifiConfig.ssid = preferences.getString("ssid", ""); wifiConfig.password = preferences.getString("password", ""); Serial.println("读取保存的WiFi配置:"); Serial.print("SSID: "); Serial.println(wifiConfig.ssid); Serial.print("Password: "); Serial.println(getPasswordDisplay()); // 配置WiFi模式 WiFi.mode(WIFI_AP_STA); // 如果没有保存的配置,启动AP模式 if (wifiConfig.ssid.length() == 0) { Serial.println("未找到WiFi配置,启动AP模式..."); WiFi.softAP("ESP32-S3-Config", "12345678"); Serial.print("AP IP地址: "); Serial.println(WiFi.softAPIP()); } // 创建独立任务(增加堆栈大小) xTaskCreatePinnedToCore( wifiTask, // 任务函数 "WiFiTask", // 任务名称 12288, // 增加堆栈大小 NULL, // 参数 2, // 提高优先级 NULL, // 任务句柄 0 // 核心 ); xTaskCreatePinnedToCore( webServerTask, // 任务函数 "WebServerTask", // 任务名称 24576, // 增加堆栈大小 NULL, // 参数 1, // 优先级 NULL, // 任务句柄 1 // 核心 ); Serial.println("任务创建完成,系统正常运行"); } void loop() { // 主循环为空,所有工作都在任务中处理 delay(10000); // 长时间延迟,减少主循环执行频率 }
最新发布
10-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值