当前位置: 首页 > news >正文

文件系统事件监听

文件系统事件和网络IO事件一样,也可以通过epoll或者IOCP 事件管理器统一调度,当所监控的文件或文件夹发生了增删改的事件时,就会触发事件回调,进行事件处理。很常见的应用,如配置文件立即生效功能,就可以通过这种方式触发软件内部进行配置更新。也可以用来监控日志文件的更新,拉取最新日志(类似notepad++打开日志文件,如果日志文件更新了,就会提示reload)。

下面分别给出了linux和windows系统下文件系统事件监听的示意代码,仅供参考。

//file: FileMonitor.h#ifndef INOTIFY_FILEMONITOR_H
#define INOTIFY_FILEMONITOR_H#include <string>
#include <map>/*** 文件监视器。* 用于监控文件系统中文件相关的事件,如创建、删除等操作。*/
class FileMonitor {public:FileMonitor() = default;~FileMonitor() = default;void startWatch(const std::string &path);void stopWatch();bool addWatch(const std::string &path);private:// inotify 句柄int inotify_fd{0};// inotify_add_watch返回值 -> 要监控的文件名 的映射std::map<int, std::string> watchDesc2Name;// 是否停止监控bool isStopped{false};
};#endif //INOTIFY_FILEMONITOR_H// file: FileMonitor.cpp#include <sys/inotify.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <thread>
#include <chrono>
#include <cstdio>
#include <iostream>
#include "FileMonitor.h"#define log(format, ...) { \auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());   \char buf[64];                                                                        \strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", std::localtime(&now));               \printf("%s | %llu | ", buf, std::this_thread::get_id());                             \printf(format, ##__VA_ARGS__);                                                       \printf("\n"); }#define EVENT_SIZE  ( sizeof (struct inotify_event) )
#define BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )void FileMonitor::startWatch(const std::string &path) {log("Ready to watch...");// inotify 初始化inotify_fd = inotify_init();if (!addWatch(path)) {return;}// 创建一个 epoll 句柄log("epoll_create");int epfd = epoll_create(256);struct epoll_event ev;ev.data.fd = inotify_fd;        // 设置要处理的事件相关的文件描述符ev.events = EPOLLIN | EPOLLET;  // 设置要处理的事件类型// 注册 epoll 事件log("epoll_ctl");epoll_ctl(epfd, EPOLL_CTL_ADD, inotify_fd, &ev);// 循环监听事件char buffer[BUF_LEN];struct epoll_event events[20];  // 存储从内核得到的事件集合while (!isStopped) {log("epoll_wait...");// 等待事件发生。返回需要处理的事件数目int nfds = epoll_wait(epfd, events, 20, 500);log("Event count: %d", nfds);for (int i = 0; i < nfds; ++i) {/*** epoll_wait 会一直阻塞直到下面2种情况:*   1. 一个文件描述符触发了事件。*   2. 被一个信号处理函数打断,或者 timeout 超时。* 所以下面需要对 fd 进行过滤,判断是否是我们需要的 fd 产生了事件*/if (events[i].data.fd != inotify_fd) {log("Other event, fd=%d", events[i].data.fd);continue;}long length = read(inotify_fd, buffer, BUF_LEN);if (length < 0) {perror("Read failed");}log("Read length: %ld", length);int pos = 0;int count = 0;while (pos < length) {struct inotify_event *event = (struct inotify_event *) &buffer[pos];log("[%d]event->len=%u, event->mask=%#X, EVENT_SIZE=%lu", ++count, event->len, event->mask, EVENT_SIZE);if (event->len) {if (event->mask & IN_CREATE) {log("CREATE: %s", event->name);} else if (event->mask & IN_DELETE) {log("DELETE: %s", event->name);} else if (event->mask & IN_MODIFY) {log("MODIFY: %s", event->name);} else if (event->mask & IN_OPEN) {log("OPEN: %s", event->name);} else if (event->mask & IN_CLOSE) {log("CLOSE: %s", event->name);} else {log("Unknown event, mask=%u", event->mask);}}pos += EVENT_SIZE + event->len;}}}log("Stop inotify");for (auto &ele: watchDesc2Name) {inotify_rm_watch(inotify_fd, ele.first);}close(epfd);close(inotify_fd);
}void FileMonitor::stopWatch() {isStopped = true;
}bool FileMonitor::addWatch(const std::string &path) {// TODO 需要对文件(夹)是否存在进行判断// 监听指定目录下的修改、创建、删除事件int wd = inotify_add_watch(inotify_fd, path.c_str(), IN_MODIFY | IN_CREATE | IN_DELETE);
//    int wd = inotify_add_watch(inotify_fd, path.c_str(), IN_ALL_EVENTS);if (wd < 0) {log("inotify_add_watch failed: %s", path.c_str());return false;}log("Add watch: %s", path.c_str());watchDesc2Name[wd] = path;return true;
}// main: main.cpp
#include <iostream>
#include "FileMonitor.h"int main() {std::string dirPath = "/home/nvidia/logs";FileMonitor fileMonitor;fileMonitor.startWatch(dirPath);return 0;
}// https://github.com/BornToDeath/cpp/blob/main/test/case11_inotify/main.cpp
#include <iostream>
#include <map>
#include <windows.h>
#define DIRECTORY_PATH      L"C://TestFolder"#define CONTAINING_RECORD(address, type, field) ((type *)( \(PCHAR)(address) - \(ULONG_PTR)(&((type *)0)->field)))typedef struct directory_info
{HANDLE    hDir;CHAR          szDirName[MAX_PATH];CHAR          szBuffer[4096];DWORD      dwBufLength;OVERLAPPED Overlapped;  // 此处需要定义overlapp结构
}DIRECTORY_INFO, * LPDIRECTORY_INFO;class cFileUpdateChecker
{
public:cFileUpdateChecker(const wchar_t* root) :hThread(NULL), dwThread(0), hComp(NULL), pdir(NULL) { wsprintf(dirPath,L"%s",root); }~cFileUpdateChecker(){if (hComp) {PostQueuedCompletionStatus(hComp, 0, NULL, NULL);CloseHandle(hComp);}if (hThread)        CloseHandle(hThread);if (pdir){CloseHandle(pdir->hDir);HeapFree(GetProcessHeap(), 0, pdir);}for(auto it = opened_files.begin();it!=opened_files.end();it++){if(it->second != NULL)CloseHandle(it->second);}}// 定义IOCP的工作线程static DWORD WINAPI  ThreadFunc(void* p){cFileUpdateChecker* pHost = (cFileUpdateChecker*)p;LPDIRECTORY_INFO lpdir = NULL;DWORD nbytes, errCode;LPOVERLAPPED lpOverlapped = NULL;PFILE_NOTIFY_INFORMATION fni = NULL;ULONG_PTR completion_key;DWORD wait = 1000;while (true){BOOL bRet = GetQueuedCompletionStatus(pHost->hComp, &nbytes, &completion_key, &lpOverlapped, wait);if (FALSE == bRet ){ errCode = GetLastError();if (errCode == WAIT_TIMEOUT){wait = INFINITE;continue;}elsebreak;}if (lpOverlapped == NULL){break;}else{lpdir = CONTAINING_RECORD(lpOverlapped, DIRECTORY_INFO, Overlapped);if (lpdir){fni = (PFILE_NOTIFY_INFORMATION)lpdir->szBuffer;int filename_length =fni->FileNameLength;wchar_t* root_dir = (wchar_t*)lpdir->szDirName;wchar_t* filename = &fni->FileName[0];switch (fni->Action){case FILE_ACTION_ADDED:wprintf(L"The file %s/%s was added to the directory.\r\n", root_dir, filename);break;case FILE_ACTION_REMOVED:wprintf(L"The file  %s/%s  was removed from the directory.\r\n", root_dir, filename);break;case FILE_ACTION_MODIFIED:{wchar_t file_path_buffer[MAX_PATH]={};wsprintf(file_path_buffer,L"%s\\%s",root_dir, filename);wprintf(L"The file %s was modified. This can be a change in the time stamp or attributes.\r\n", file_path_buffer);auto it = pHost->opened_files.find(file_path_buffer);if(it == pHost->opened_files.end()){HANDLE hFile = CreateFile(file_path_buffer,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if(hFile != NULL){DWORD dwFilePointer = SetFilePointer(hFile,0,NULL,FILE_END);if(dwFilePointer != INVALID_SET_FILE_POINTER)pHost->opened_files[file_path_buffer]=hFile;elseCloseHandle(hFile);}}else{CHAR szReadText[50] = {};DWORD dwRead = 0;//需要注意的是,FILE_ACTION_MODIFIED信号仅表示文件系统中的该文件节点对象的写状态更新了,至于是否完成落盘,还需要看修改文件的进程是否完成了刷盘。由于实时性问题,刷盘动作可能存在一定的滞后,如果每次进入Modify事件都要增量读取更新的文件内容。可以增加30~50ms的延时bRet = ReadFile(it->second,szReadText,32,&dwRead,NULL);if(bRet == FALSE){CloseHandle(it->second);pHost->opened_files.erase(it);}else{printf("Additional file content: %s.\r\n",szReadText);SetFilePointer(it->second,0,NULL,FILE_END);}}}break;case FILE_ACTION_RENAMED_OLD_NAME:{const wchar_t* new_filename = L"";int new_filename_length = 0;if (fni->NextEntryOffset);{PFILE_NOTIFY_INFORMATION new_fni = (PFILE_NOTIFY_INFORMATION)(lpdir->szBuffer + fni->NextEntryOffset);new_filename = &new_fni->FileName[0];new_filename_length = new_fni->FileNameLength;}wprintf(L"The file  %s/%s  was renamed, and this is the new name %s.\r\n", root_dir, filename, new_filename);}break;case FILE_ACTION_RENAMED_NEW_NAME:wprintf(L"The file  %s/%s  was renamed and this is the new name.\r\n", root_dir,  filename);break;default:break;}// re post eventZeroMemory(&(pHost->pdir->Overlapped), sizeof(OVERLAPPED));ZeroMemory(pHost->pdir->szBuffer, 4096);ReadDirectoryChangesW(pHost->pdir->hDir,pHost->pdir->szBuffer,sizeof(pHost->pdir->szBuffer),TRUE,FILE_NOTIFY_CHANGE_FILE_NAME |FILE_NOTIFY_CHANGE_DIR_NAME |FILE_NOTIFY_CHANGE_ATTRIBUTES |FILE_NOTIFY_CHANGE_SIZE |FILE_NOTIFY_CHANGE_LAST_ACCESS |FILE_NOTIFY_CHANGE_CREATION |FILE_NOTIFY_CHANGE_SECURITY |FILE_NOTIFY_CHANGE_LAST_WRITE,&(pHost->pdir->dwBufLength),(OVERLAPPED*)(&(pHost->pdir->Overlapped)),NULL);}}}return 0;}bool StartChecker(){hComp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);pdir = (LPDIRECTORY_INFO)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DIRECTORY_INFO));if (NULL == pdir){return false;}pdir->hDir = CreateFile(dirPath,FILE_LIST_DIRECTORY,FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,NULL);if (pdir->hDir == INVALID_HANDLE_VALUE){return false;}// 启动工作线程hThread = CreateThread(NULL, 0, ThreadFunc, this, 0, &dwThread);if (NULL == hThread){return false;}ZeroMemory(&(pdir->Overlapped), sizeof(OVERLAPPED));ZeroMemory(pdir->szBuffer, 4096);ZeroMemory(pdir->szDirName, MAX_PATH);wsprintf((wchar_t*)pdir->szDirName,L"%s", dirPath);// 投递I/O请求到完成端口HANDLE hRet = CreateIoCompletionPort(pdir->hDir, hComp,NULL, 0);if (NULL == hComp){return false;}BOOL bRet = ReadDirectoryChangesW(pdir->hDir,pdir->szBuffer,4096,TRUE,FILE_NOTIFY_CHANGE_FILE_NAME |FILE_NOTIFY_CHANGE_DIR_NAME |FILE_NOTIFY_CHANGE_ATTRIBUTES |FILE_NOTIFY_CHANGE_SIZE |FILE_NOTIFY_CHANGE_LAST_ACCESS |FILE_NOTIFY_CHANGE_CREATION |FILE_NOTIFY_CHANGE_SECURITY |FILE_NOTIFY_CHANGE_LAST_WRITE,&pdir->dwBufLength,&(pdir->Overlapped),NULL);if (FALSE == bRet){return false;}return true;}
private:HANDLE hThread;DWORD dwThread;HANDLE  hComp;LPDIRECTORY_INFO pdir;WCHAR dirPath[MAX_PATH];std::map<std::wstring,HANDLE> opened_files;
};int main()
{cFileUpdateChecker* ckr = new cFileUpdateChecker(DIRECTORY_PATH);std::cout << "Supervise "; std::wcout << DIRECTORY_PATH << " Begin..." << std::endl;ckr->StartChecker();getchar();delete ckr;getchar();
}// https://blog.csdn.net/nhn_devlab/article/details/6034055

相关文章:

文件系统事件监听

文件系统事件和网络IO事件一样&#xff0c;也可以通过epoll或者IOCP 事件管理器统一调度&#xff0c;当所监控的文件或文件夹发生了增删改的事件时&#xff0c;就会触发事件回调&#xff0c;进行事件处理。很常见的应用&#xff0c;如配置文件立即生效功能&#xff0c;就可以通…...

探秘HTTPS:如何通过SSL/TLS保证网络通信安全

目录 引言 详解HTTPS加密实现机制 SSL/TLS工作原理 结论 引言 随着网络安全威胁的日益增加&#xff0c;HTTPS通过SSL&#xff08;Secure Sockets Layer&#xff09;和TLS&#xff08;Transport Layer Security&#xff09;协议提供的加密技术变得至关重要。这些技术保证了用…...

Java算法之动态规划

Java算法之动态规划 前言 ​ 最近这一段时间一直在刷算法题&#xff0c;基本上一有时间就会做一两道&#xff0c;这两天做了几道动态规划的问题&#xff0c;动态规划之前一直是我比较头疼的一个问题&#xff0c;感觉好复杂&#xff0c;一遇到这样的问题就想跳过&#xff0c;昨…...

C++从零开始的打怪升级之路(day47)

这是关于一个普通双非本科大一学生的C的学习记录贴 在此前&#xff0c;我学了一点点C语言还有简单的数据结构&#xff0c;如果有小伙伴想和我一起学习的&#xff0c;可以私信我交流分享学习资料 那么开启正题 今天分享的是关于set和map的知识点 1.关联式容器 在前面&#…...

香橙派AIpro开发板开箱测评

2023年12月&#xff0c;香橙派联合华为发布了基于昇腾的Orange Pi AIpro开发板&#xff0c;提供8/20TOPS澎湃算力&#xff0c;能覆盖生态开发板者的主流应用场景&#xff0c;让用户实践各种创新场景&#xff0c;并为其提供配套的软硬件。香橙派AIpro开发板一经发布便吸引了众多…...

ISP基础概述

原文来自ISP 和摄像头基本知识 本文主要介绍ISP&#xff0c;以供读者能够理解该技术的定义、原理、应用。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;计算机杂记 &#x1f380;CSDN主页 发狂的小花 &#x1f3…...

C++第一弹---C++入门(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 【C详解】 C入门 1、C关键字(C98) 2、命名空间 2.1、命名空间定义 2.2、命名空间使用 3、C输入&输出 4、缺省参数 4.1、缺省参数概念 4.2、缺省参…...

VScode格式化快捷键

vscode格式化代码快捷键 如何使用快捷键格式化代码。使用Java的格式去设置&#xff0c;发现不起作用。 在这里记录一下&#xff1a; 在Windows中&#xff0c;vscode格式化代码快捷键是“ShiftAltF”&#xff1b; 在Mac中&#xff0c;vscode格式化代码快捷键是“ShiftOption…...

HCIP---IS-IS协议

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 一.IS-IS协议概述 IS-IS是一种基于链路状态的内部网关协议&#xff08;IGP&#xff09;&#xff0c;它使用最短路径优先算法&#xff08;SPF或Dijkstra&#xff09;进行路由计算。这种协议在自治…...

突破编程_C++_设计模式(组合模式)

1 组合模式的基本概念 C中的组合模式是一种对象结构型模式&#xff0c;它将多个对象组合成树形结构&#xff0c;以表示具有整体-部分关系的层次结构。在这个模式中&#xff0c;对单个对象&#xff08;叶子对象&#xff09;与组合对象&#xff08;容器对象&#xff09;的使用具…...

010Editor汉化版+下载+注册码+模板bug

项目场景&#xff1a; 这天我想使用我的不知名的一个破解版本的010Edit来查看一个EXE程序&#xff0c;并想使用模板功能&#xff0c;但是发现没有该模板还无法下载最新模板 问题描述 010Edit联网后需要注册码&#xff1a; 010 Editor 激活码生成器 使用方法 参照教程使用0…...

js【详解】BOM

浏览器对象模型 &#xff08;Browser obiect Mode 简称 BOM&#xff09; 浏览器对象即 window&#xff0c;调用window对象的属性和方法时&#xff0c;可以省略window window 常用的属性 Navigator 常用于获取浏览器的信息 navigator.userAgent;火狐浏览器范例&#xff1a; “…...

Leetcode 3077. Maximum Strength of K Disjoint Subarrays

Leetcode 3077. Maximum Strength of K Disjoint Subarrays 1. 解题思路 1. 朴素思路2. 算法优化 2. 代码实现 题目链接&#xff1a;3077. Maximum Strength of K Disjoint Subarrays 1. 解题思路 这道题很惭愧没有搞定&#xff0c;思路上出现了差错&#xff0c;导致一直没能…...

【JetsonNano】onnxruntime-gpu 环境编译和安装,支持 Python 和 C++ 开发

1. 设备 2. 环境 sudo apt-get install protobuf-compiler libprotoc-devexport PATH/usr/local/cuda/bin:${PATH} export CUDA_PATH/usr/local/cuda export cuDNN_PATH/usr/lib/aarch64-linux-gnu export CMAKE_ARGS"-DONNX_CUSTOM_PROTOC_EXECUTABLE/usr/bin/protoc&qu…...

知名比特币质押协议项目Babylon确认参加Hack.Summit()2024区块链开发者大会

Babylon项目已确认将派遣其项目代表出席2024年在香港数码港举办的Hack.Summit()2024区块链开发者大会。作为比特币生态的领军项目&#xff0c;Babylon积极参与全球区块链领域的交流与合作&#xff0c;此次出席大会将为其提供一个展示项目进展、交流技术与创新思路的重要平台。B…...

如何学习、上手点云算法(三):用VsCode、Visual Studio来debug基于PCL、Open3D的代码

写在前面 本文内容 以PCL 1.14.0&#xff0c;Open3D0.14.1为例&#xff0c;对基于PCL、Open3D开发的代码进行源码debug&#xff1b; 如何学习、上手点云算法系列&#xff1a; 如何学习、上手点云算法(一)&#xff1a;点云基础 如何学习、上手点云算法(二)&#xff1a;点云处理相…...

【干货】alzet渗透泵操作说明

alzet渗透泵是一款小型、可植入式的胶囊渗透泵产品&#xff0c;此产品由于其独特的渗透原理&#xff0c;深受广大科研人员的喜爱。该泵可适用于小鼠、大鼠及其他实验动物的研究&#xff0c;并且alzet渗透泵可减轻科研人员夜间及周末给药的困扰。alzet渗透泵无需外部连接或频繁处…...

CVPR 2022 Oral | Bailando: 基于编舞记忆和Actor-Critic GPT的3D舞蹈生成

目录 测试结果&#xff1a; 02 提出的方法 测试结果&#xff1a; 预测有3个步骤&#xff0c;速度比较慢 02 提出的方法 1. 针对舞蹈序列的VQ-VAE和编舞记忆 与之前的方法不同&#xff0c;我们不学习从音频特征到 3D 关键点序列的连续域的直接映射。相反&#xff0c;我们先让…...

解读电影级视频生成模型 MovieFactory

Diffusion Models视频生成-博客汇总 前言:MovieFactory是第一个全自动电影生成模型,可以根据用户输入的文本信息自动扩写剧本,并生成电影级视频。其中针对预训练的图像生成模型与视频模型之间的gap提出了微调方法非常值得借鉴。这篇博客详细解读一下这篇论文《MovieFactory:…...

【Python从入门到进阶】50、当当网Scrapy项目实战(三)

接上篇《49、当当网Scrapy项目实战&#xff08;二&#xff09;》 上一篇我们讲解了的Spider与item之间的关系&#xff0c;以及如何使用item&#xff0c;以及使用pipelines管道进行数据下载的操作&#xff0c;本篇我们来讲解Scrapy的多页面下载如何实现。 一、多页面下载原理分…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...

Docker拉取MySQL后数据库连接失败的解决方案

在使用Docker部署MySQL时&#xff0c;拉取并启动容器后&#xff0c;有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致&#xff0c;包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因&#xff0c;并提供解决方案。 一、确认MySQL容器的运行状态 …...

ubuntu22.04有线网络无法连接,图标也没了

今天突然无法有线网络无法连接任何设备&#xff0c;并且图标都没了 错误案例 往上一顿搜索&#xff0c;试了很多博客都不行&#xff0c;比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动&#xff0c;重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...

前端开发者常用网站

Can I use网站&#xff1a;一个查询网页技术兼容性的网站 一个查询网页技术兼容性的网站Can I use&#xff1a;Can I use... Support tables for HTML5, CSS3, etc (查询浏览器对HTML5的支持情况) 权威网站&#xff1a;MDN JavaScript权威网站&#xff1a;JavaScript | MDN...

[拓扑优化] 1.概述

常见的拓扑优化方法有&#xff1a;均匀化法、变密度法、渐进结构优化法、水平集法、移动可变形组件法等。 常见的数值计算方法有&#xff1a;有限元法、有限差分法、边界元法、离散元法、无网格法、扩展有限元法、等几何分析等。 将上述数值计算方法与拓扑优化方法结合&#…...

MLP实战二:MLP 实现图像数字多分类

任务 实战&#xff08;二&#xff09;&#xff1a;MLP 实现图像多分类 基于 mnist 数据集&#xff0c;建立 mlp 模型&#xff0c;实现 0-9 数字的十分类 task: 1、实现 mnist 数据载入&#xff0c;可视化图形数字&#xff1b; 2、完成数据预处理&#xff1a;图像数据维度转换与…...