从 0 实现一个文件搜索工具 (Java 项目)

背景

各文件系统下, 都有提供文件查找的功能, 但是一般而言搜索速度很慢
在这里插入图片描述
本项目仿照 everything 工具, 实现本地文件的快速搜索

实现功能

  1. 选择指定本地目录, 根据输入的信息, 进行搜索, 显示指定目录下的匹配文件信息
  2. 文件夹包含中文时, 支持汉语拼音搜索 (全拼 / 首字母匹配)

相关技术

Java + Servlet + Pinyin4j
JDBC + SQLite (SQLite 相对于 MySQL 更加轻量, 并且引入 jar 包即可使用, 不必安装配套应用)
JavaFx

数据库设计

在这里插入图片描述

SQLite 创建 SQL 的语句如下

create table if not exists file_meta (id INTEGER primary key autoincrement,name varchar(50) not null,path varchar(512) not null,is_directory boolean not null,pinyin varchar(100) not null,pinyin_first varchar(50) not null,size BIGINT not null,last_modified timestamp not null
);

项目的基本框架

在这里插入图片描述

前端页面

在这里插入图片描述

app.fxml 文件

显示界面的图画化结构

<?xml version="1.0" encoding="UTF-8"?><?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?><?import javafx.geometry.Insets?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<GridPane fx:controller="gui.GUIController" fx:id="gridPane" vgap="10" alignment="center" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"><children ><Button fx:id="button"  onMouseClicked="#choose" prefWidth="90" text="选择目录" GridPane.rowIndex="0" GridPane.columnIndex="0"></Button><Label fx:id="label" text="当前未选择目录" GridPane.rowIndex="0" GridPane.columnIndex="0"><GridPane.margin><Insets left="100"></Insets></GridPane.margin></Label><TextField fx:id="textField" prefWidth="900" GridPane.rowIndex="1" GridPane.columnIndex="0" ></TextField><TableView fx:id="tableView" prefWidth="900" prefHeight="700" GridPane.rowIndex="2" GridPane.columnIndex="0"><columns><TableColumn prefWidth="220" text="文件名"><cellValueFactory><PropertyValueFactory property="name"></PropertyValueFactory></cellValueFactory></TableColumn><TableColumn prefWidth="400" text="路径"><cellValueFactory><PropertyValueFactory property="path"></PropertyValueFactory></cellValueFactory></TableColumn><TableColumn prefWidth="100" text="大小"><cellValueFactory><PropertyValueFactory property="sizeText"></PropertyValueFactory></cellValueFactory></TableColumn><TableColumn prefWidth="180" text="修改时间"><cellValueFactory><PropertyValueFactory property="lastModifiedText"></PropertyValueFactory></cellValueFactory></TableColumn></columns></TableView></children>
</GridPane>

GUIController 类

与 app.fxml 文件配套使用, 该类离实现了 界面中按键的绑定事件, 以及对搜索框内容进行监听, 当搜索框内容改变时, 重新搜索, 并将结果返回到查询结果显示处 (实现动态搜索功能)

public class GUIController implements Initializable {@FXMLprivate Label label;@FXMLprivate GridPane gridPane;@FXMLprivate Button button;@FXMLprivate TextField textField;@FXMLprivate TableView<FileMeta> tableView;private SearchService searchService = null;@Overridepublic void initialize(URL location, ResourceBundle resources) {// 在这里对 输入框 加一个监听器// 需要指定对 text 这个内容属性进行监听// textField.textProperty() 获取输入框里的内容textField.textProperty().addListener(new ChangeListener<String>() {/***  会在用户每次修改 输入框内容 的时候, 被自动调用到* @param observable* @param oldValue 输入框被修改之前的值* @param newValue 输入框被修改之后的值*/@Overridepublic void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {// 此处要干的事情, 是根据新的值, 重新进行查询操作freshTable(newValue);}});}private void freshTable(String query) {// 重新查询数据库, 把查询结果, 设置到表格中if(searchService == null) {System.out.println("searchService 尚未初始化, 不能查询!");return;}// 把之前表里的内容清空掉ObservableList<FileMeta> fileMetas = tableView.getItems();fileMetas.clear();List<FileMeta> results = searchService.search(query);fileMetas.addAll(results);}/*** 使用该方法, 作为鼠标点击事件的回调函数* @param mouseEvent*/public void choose(MouseEvent mouseEvent) {// 创建一个 目录选择器DirectoryChooser directoryChooser = new DirectoryChooser();// 把该对话框显示出来Window window = gridPane.getScene().getWindow();File file = directoryChooser.showDialog(window);if(file == null) {System.out.println("用户选择的路径为空");} else {System.out.println(file.getAbsolutePath());}// 把用户选择的路径,显示到 label 中label.setText(file.getAbsolutePath());// 如果不是首次扫描, 就应该停止上次扫描任务, 执行本次扫描任务if(searchService != null) {searchService.shutdown();}// 对用户选择的路径进行扫描, 初始化searchService = new SearchService();searchService.init(file.getAbsolutePath());}
}

GUIClient

继承 Application 方法, 为界面启动类, 调用 javafx 提供的 launch 方法来启动整个程序

public class GUIClient extends Application {/*** 程序启动时, 会立即执行的方法* @param primaryStage* @throws Exception*/@Overridepublic void start(Stage primaryStage) throws Exception {// 加载 fxml 文件, 把 fxml 文件里的内容, 给设置到舞台中Parent parent = FXMLLoader.load(GUIClient.class.getClassLoader().getResource("app.fxml"));primaryStage.setScene(new Scene(parent, 1000, 800));primaryStage.setTitle("文件搜索工具");// 准备工作完成, 显示场景界面primaryStage.show();}public static void main(String[] args) {// 调用 javafx 提供的 launch 方法来启动整个程序launch(args);}
}

后端代码

实体类

FileMeta

本类对应着数据库的 file_meta 表
因为没引入 lombok, 因此只能手写 Setter 和 Getter 方法

// 本类的示例就代表 file_meta 表里的每个记录.
public class FileMeta {private int id;private String name;private String path;private boolean isDirectory;// 这里存储的 size 是字节, 但是界面上输出的不应该以字节位单位, k, m, gprivate long size;// 这个存储的是时间戳(机器能看懂)private long lastModified;// 这个是进行格式化转换之后的时间格式(人能看懂的)
//    private long lastModifiedText;// 构造方法public FileMeta(String name, String path, boolean isDirectory, long size, long lastModified) {this.name = name;this.path = path;this.isDirectory = isDirectory;this.size = size;this.lastModified = lastModified;}public FileMeta(File f) {this(f.getName(), f.getParent(), f.isDirectory(), f.length(), f.lastModified());}public String getPinyin() {return PinyinUtil.get(name, true);}public String getPinyinFirst() {return PinyinUtil.get(name, false);}public String getSizeText() {// 常见单位: Byte, KB, MB, GB, TB// 如果 size < 1024, 使用 Byte// 如果 1024 <= size < 1024*1024, 使用 MB// ...double curSize = size;String[] units = {"Byte", "KB", "MB", "GB", "TB"};for(int i=0;i<units.length;i++) {if(curSize < 1024) {return String.format("%.2f " + units[i], new BigDecimal(curSize));}curSize /= 1024;}return String.format("%.2f TB", new BigDecimal(curSize));}public String getLastModifiedText() {DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return dateFormat.format(lastModified);}// -------------------------------public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public boolean isDirectory() {return isDirectory;}public void setDirectory(boolean directory) {isDirectory = directory;}public long getSize() {return size;}public void setSize(long size) {this.size = size;}public long getLastModified() {return lastModified;}public void setLastModified(long lastModified) {this.lastModified = lastModified;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;FileMeta fileMeta = (FileMeta) o;return isDirectory == fileMeta.isDirectory  && name.equals(fileMeta.name) && path.equals(fileMeta.path);}@Overridepublic int hashCode() {return Objects.hash(name, path, isDirectory);}
}

DBUtil

本类封装 JDBC 的 连接/关闭/管理 操作

public class DBUtil {// 使用 单例模式(懒汉模式) 来提供 DataSourceprivate static volatile DataSource dataSource = DBUtil.getDataSource();// 创建数据源: Datasourcepublic static DataSource getDataSource() {if (dataSource == null) { //外层 if 判断是否要加锁 (加锁是要消耗资源的, if判断一下比 synchronized 加一次锁消耗资源要少的多)synchronized (DBUtil.class) {if(dataSource == null ) { //内层 if 判断是否要创建 DataSourcedataSource = new SQLiteDataSource();((SQLiteDataSource)dataSource).setUrl("jdbc:sqlite://D:/AAASpringBootProject/sqlite/fileSearcher.db");}}}return dataSource;}// 建立连接public static Connection getConnection() throws SQLException {return dataSource.getConnection();}// 断开连接public static void close(Connection connection, Statement statement, ResultSet resultSet) {if(resultSet != null) {try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}if(statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if(connection != null ) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}
}

FileDao 类

通过该类来实现数据库的增删改查操作

// 通过这个类来封装针对 file_meta 表的操作
public class FileDao {// 1.初始化数据库 (建表)public void initDB() {// 1) 先能够读取到 db.sql 中的 SQL 语句// 2) 根据 SQL 语句调用 jdbc 执行操作Connection connection = null;Statement statement = null;try {connection = DBUtil.getConnection();// 使用 connection.createStatement() 来执行建库建表 sqlstatement = connection.createStatement();String[] sqls = getInitSql();for(String sql : sqls) {System.out.println("[initDB] sql:" + sql);statement.executeUpdate(sql); //该方式用来执行一些基本不变的sql语句}} catch (SQLException | IOException e) {e.printStackTrace();} finally {DBUtil.close(connection, statement, null);}}// 从 db.sql 中读取文件内容// 一个 sql 语句对应一个 String, 多个 sql 语句对应 String[]private String[] getInitSql() throws IOException {// 用这个 StringBuilder 来存储最终结果StringBuilder stringBuilder = new StringBuilder();// 此处需要动态获取到 db.sql 文件的路径, 而不是一个写死的绝对路径(运行在别人的电脑上的)try(InputStream inputStream = FileDao.class.getClassLoader().getResourceAsStream("db.sql")) {// 这里是字节流到字符流的转换(对字符能轻松的进行操作, 对字节的操作要难得多)try(InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf8")) {while(true) {// char 类型读取不到 -1, 也就没有文件读取结束的标记了, 所以这里使用 int 来接收int ch = inputStream.read(); //inputStream.read() 读取到文件结束符会返回 -1if( ch == -1) break;  //文件读取完毕stringBuilder.append((char) ch);}}} catch (IOException e) {e.printStackTrace();}// sql 语句以 ';' 结束, 我们就以 ';' 来拆分字符串, 一句 sql 就是一个 Stringreturn stringBuilder.toString().split(";");}// 2.插入文件/目录数据到数据库中//   此处提供"批量插入"操作public void add(List<FileMeta> fileMetas) {Connection connection = null;PreparedStatement statement = null;try {connection = DBUtil.getConnection();// 关闭自动提交功能// (一次插入多条数据, 如果一个一个插,数据库就要打开-关闭-打开-关闭... 因此一次插入多条数据, 会减小数据库的资源消耗)connection.setAutoCommit(false);String sql = "insert into file_meta values(null, ?, ?, ?, ?, ?, ?, ?)";statement = connection.prepareStatement(sql);for(FileMeta fileMeta : fileMetas) {// 把当前 FileMeta 对象, 替换到 SQL 语句中.statement.setString(1,fileMeta.getName());statement.setString(2,fileMeta.getPath());statement.setBoolean(3, fileMeta.isDirectory());statement.setString(4,fileMeta.getPinyin());statement.setString(5,fileMeta.getPinyinFirst());statement.setLong(6, fileMeta.getSize());statement.setTimestamp(7, new Timestamp(fileMeta.getLastModified()));// 使用 addBatch 把构造好的片段连接起来// addBatch 会把已经构造好的 SQL 保存起来, 同时又会允许重新构造一个新的 SQL 出来statement.addBatch();System.out.println("[insert] 插入文件: " + fileMeta.getPath() + File.separator + fileMeta.getName());}// 执行所有的 sql 片段statement.executeBatch();// 执行完毕后, 通过 commit 告诉数据库, 添加完毕, 执行上述 batch 操作(自动提交已经关闭了)connection.commit();} catch (SQLException e) {try {if(connection != null) {// 如果连接已建立, 并且出现异常, 那就是提交的内容有错误, 此时进行回滚操作connection.rollback();}} catch (SQLException ex) {ex.printStackTrace();}} finally {DBUtil.close(connection, statement, null);}}/***  3.按照特定的关键词进行查询*  此处查询 pattern , 可能是文件名的一部分, 可能是文件名拼音的一部分, 也可能是拼音首字母的一部分 ...* @param pattern 根据 pattern 查询数据库匹配内容* @return*/public List<FileMeta> searchByPattern(String pattern) {List<FileMeta> fileMetas = new ArrayList<>();Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {connection = DBUtil.getConnection();String sql = "select name, path, is_directory, size, last_modified from file_meta " +" where name like ? or pinyin like ? or pinyin_first like ? " +" order by path, name";statement = connection.prepareStatement(sql);statement.setString(1,"%" + pattern + "%");statement.setString(2,"%" + pattern + "%");statement.setString(3,"%" + pattern + "%");resultSet = statement.executeQuery();while(resultSet.next()) {String name = resultSet.getString("name");String path = resultSet.getString("path");boolean isDirectory = resultSet.getBoolean("is_directory");long size = resultSet.getLong("size");Timestamp lastModified = resultSet.getTimestamp("last_modified");FileMeta fileMeta = new FileMeta(name, path, isDirectory, size, lastModified.getTime());fileMetas.add(fileMeta);}} catch (SQLException e) {e.printStackTrace();} finally {DBUtil.close(connection, statement, resultSet);}return fileMetas;}/***  根据给定路径查询结果.* @param targetPath 给定路径* @return 该路径下的所有文件信息(一层)*/public List<FileMeta> searchByPath(String targetPath) {List<FileMeta> fileMetas = new ArrayList<>();Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {connection = DBUtil.getConnection();String sql = "select name, path, is_directory, size, last_modified from file_meta " +" where path = ?";statement = connection.prepareStatement(sql);statement.setString(1, targetPath);resultSet = statement.executeQuery();while(resultSet.next()) {String name = resultSet.getString("name");String path = resultSet.getString("path");boolean isDirectory = resultSet.getBoolean("is_directory");long size = resultSet.getLong("size");Timestamp lastModified = resultSet.getTimestamp("last_modified");FileMeta fileMeta = new FileMeta(name, path, isDirectory, size, lastModified.getTime());fileMetas.add(fileMeta);}} catch (SQLException e) {e.printStackTrace();} finally {DBUtil.close(connection, statement, resultSet);}return fileMetas;}/***  发现某个文件已从磁盘上删除, 此时需要把对应表里的内容删除掉* @param fileMetas 删除的 文件/目录*/public void delete(List<FileMeta> fileMetas) {Connection connection = null;PreparedStatement statement = null;try {connection = DBUtil.getConnection();connection.setAutoCommit(false);  // 将自动提交关闭, 把下列批量删除操作看作一个事务进行for(FileMeta fileMeta : fileMetas) {String sql = null;if(!fileMeta.isDirectory()) {// 对文件的sql语句构造sql = "delete from file_meta where name = ? and path = ?";statement = connection.prepareStatement(sql);statement.setString(1, fileMeta.getName());statement.setString(2, fileMeta.getPath());} else {// 对目录的sql语句构造sql = "delete from file_meta where (name = ? and path = ?) or (path like ?)";statement = connection.prepareStatement(sql);statement.setString(1, fileMeta.getName());statement.setString(2, fileMeta.getPath());statement.setString(3, fileMeta.getPath() + File.separator + fileMeta.getName() + File.separator + "%");}statement.executeUpdate();System.out.println("[delete] " + fileMeta.getPath() + "\\" + fileMeta.getName());// 此处对于每个 statement 对象都要单独关闭// (每个 statement 都可能是不同的 sql 语句, 以前可以统一关闭是因为 sql 模板相同, 只是填充参数不同, 修改一下参数就可以接着用)statement.close();}// 告知数据库, 事务构造完毕, 进行统一提交connection.commit();} catch (SQLException e) {try {connection.rollback();} catch (SQLException ex) {ex.printStackTrace();}} finally {DBUtil.close(connection, null, null);}}
}

工具类

PinyinUtil

封装 Pinyin4j 的功能
汉字 => 拼音 (全拼 / 首字母)

public class PinyinUtil {/***  获取字符串的拼音** @param src  第一个参数表示要获取拼音的字符串* @param fullSpell  第二个参数表示是否是全拼.*                   比如针对"你好啊"该字符串, true 对应全拼: nihaoa, false 对应首字母: nha*                   此处针对多音字不做过多考虑, 采用第一个元素代表的发音(也是最常用的发音)* @return  字符串的拼音*/public static String get(String src, Boolean fullSpell) {// trim() 去除字符串两侧的空白字符. eg: \t  \n  \f  \v  空格 ...if(src == null || src.trim().length() == 0) {// 空字符不做处理return null;}// 针对 Pinyin4j 做一些配置, 让他将拼音 yu 使用 v 表示HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();format.setVCharType(HanyuPinyinVCharType.WITH_V);// 遍历字符串的每个字符, 针对每个字符分别进行转换, 将转换得到的拼音, 拼接到 StringBuilder 里StringBuilder stringBuilder = new StringBuilder();src = src.trim();for(int i=0;i<src.length();i++) {// 针对单个字符进行转换char ch = src.charAt(i);String[] tmp = null;try {tmp = PinyinHelper.toHanyuPinyinStringArray(ch, format);} catch (BadHanyuPinyinOutputFormatCombination e) {e.printStackTrace();}if(tmp == null || tmp.length == 0) {// 拼音转换失败, 返回空数组// 说明当前字符就不是汉字, 可能是字母,数字或符号, eg: a, b, c, 1, 2, 3// 此时保留原始字符就好stringBuilder.append(ch);}else if(fullSpell) {stringBuilder.append(tmp[0]);}else {stringBuilder.append(tmp[0].charAt(0));}}return stringBuilder.toString();}
}

功能处理

扫描文件目录, 将目录下所有文件/目录信息存储到数据库中

当选择搜索路径后, 会递归的扫描指定路径下的所有目录及文件, 并将扫描到的 所有文件/目录信息 存储到数据库中
(查数据库比查文件系统要快, 因此其实每次查找指定文件在文件系统中出现的位置, 都是查询数据库中预存的信息)

public class GUIController implements Initializable {@FXMLprivate Label label;@FXMLprivate GridPane gridPane;@FXMLprivate Button button;@FXMLprivate TextField textField;@FXMLprivate TableView<FileMeta> tableView;private SearchService searchService = null;/*** 使用该方法, 作为鼠标点击事件的回调函数* @param mouseEvent*/public void choose(MouseEvent mouseEvent) {// 创建一个 目录选择器DirectoryChooser directoryChooser = new DirectoryChooser();// 把该对话框显示出来Window window = gridPane.getScene().getWindow();// 获取选定的文件File file = directoryChooser.showDialog(window);if(file == null) {System.out.println("用户选择的路径为空");} else {System.out.println(file.getAbsolutePath());}// 把用户选择的路径,显示到 label 中label.setText(file.getAbsolutePath());// 如果不是首次扫描, 就应该停止上次扫描任务, 执行本次扫描任务if(searchService != null) {searchService.shutdown();}// 对用户选择的路径进行扫描, 初始化searchService = new SearchService();searchService.init(file.getAbsolutePath());}
}

在这里插入图片描述

搜索框内容发生改变后, 自动进行数据库搜索, 将匹配内容展示到页面

当搜索框内容改变时, 会被系统绑定的事件监听到, 重新进行数据库搜索, 并将匹配信息作为结果返回到查询结果显示处

public class GUIController implements Initializable {@FXMLprivate Label label;@FXMLprivate GridPane gridPane;@FXMLprivate Button button;@FXMLprivate TextField textField;@FXMLprivate TableView<FileMeta> tableView;private SearchService searchService = null;@Overridepublic void initialize(URL location, ResourceBundle resources) {// 在这里对 输入框 加一个监听器// 需要指定对 text 这个内容属性进行监听// textField.textProperty() 获取输入框里的内容textField.textProperty().addListener(new ChangeListener<String>() {/***  会在用户每次修改 输入框内容 的时候, 被自动调用到* @param observable* @param oldValue 输入框被修改之前的值* @param newValue 输入框被修改之后的值*/@Overridepublic void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {// 此处要干的事情, 是根据新的值, 重新进行查询操作freshTable(newValue);}});}private void freshTable(String query) {// 重新查询数据库, 把查询结果, 设置到表格中if(searchService == null) {System.out.println("searchService 尚未初始化, 不能查询!");return;}// 把之前表里的内容清空掉ObservableList<FileMeta> fileMetas = tableView.getItems();fileMetas.clear();List<FileMeta> results = searchService.search(query);fileMetas.addAll(results);}
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/15639.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DiffMap:首个利用LDM来增强高精地图构建的网络

论文标题&#xff1a; DiffMap: Enhancing Map Segmentation with Map Prior Using Diffusion Model 论文作者&#xff1a; Peijin Jia, Tuopu Wen, Ziang Luo, Mengmeng Yang, Kun Jiang, Zhiquan Lei, Xuewei Tang, Ziyuan Liu, Le Cui, Kehua Sheng, Bo Zhang, Diange Ya…

深入分析 Android Activity (四)

深入分析 Android Activity (四) 1. Activity 的生命周期详解 Activity 的生命周期方法提供了一组回调&#xff0c;使开发者能够在不同状态下执行相应的逻辑。了解这些方法有助于开发者管理资源和确保应用程序的行为一致。 1.1 onCreate onCreate 是 Activity 的入口点&…

解决go install 网络问题

rootiZbp1hiqzlhh6w05gloffgZ:~# go install mvdan.cc/garblelatest go: mvdan.cc/garblelatest: module mvdan.cc/garble: Get "https://proxy.golang.org/mvdan.cc/garble/v/list": dial tcp 172.217.160.81:443: i/o timeout解决方法 更换阿里代理 rootiZbp1hiq…

浅论未来 IT 和财务结合的趋势

前言 会计核算企业各类业务单据&#xff0c;所有业务部门的数据都会汇流到财务部来&#xff0c;所以会计其实是企业的数据中心&#xff1b;而 IT 技术正是处理数据的利器&#xff0c;可以将重复的流程自动化&#xff0c;还能将财务数据可视化&#xff1b;因此&#xff0c;两者…

docker image prune -f 命令什么用途

docker image prune -f 命令用于清理系统中未被使用的 Docker 镜像。具体来说&#xff0c;它会删除那些未被任何容器使用的悬空镜像&#xff08;dangling images&#xff09;&#xff0c;从而释放磁盘空间。 以下是 docker image prune -f 命令的具体用途和作用&#xff1a; …

SPI通信(STM32)

一、SPI通信 &#xff11;、SPI&#xff08;Serial Peripheral Interface&#xff09;是由Motorola公司开发的一种通用数据总线 &#xff12;、四根通信线&#xff1a;SCK&#xff08;Serial Clock&#xff09;、MOSI&#xff08;Master Output Slave Input&#xff09;、MIS…

【计算理论】【《计算理论导引(原书第3版)》笔记】第〇章:绪论

文章目录 [toc]第〇章&#xff1a;绪论0.1|自动机、可计算性与复杂性计算复杂性理论可计算性理论自动机理论 0.2|数学概念和术语集合关系等价关系 图简单路径连通图圈强连通图 字符串和语言字母表上的字符串空串 w w w的反转&#xff08;倒序&#xff09; x x x和 y y y的连接字…

esp32-idf 开发踩坑记录

现象 直接使用原始命令编译idf.py build 但是提示idf 版本错误 卸载旧版本 编译出错build 问题 然后删除编译文件后&#xff0c;重新编译&#xff0c;还是出错 解决方法1 最后发现是因为项目所在文件夹有中文目录&#xff0c;把项目迁移到英文目录后&#xff0c;重新编译&a…

打破传统相亲模式,这几款靠谱的相亲软件助你脱单

相亲软件在当今社会已经变得越来越普遍&#xff0c;市面上有众多相亲软件可供选择&#xff0c;但哪些相亲软件好用呢&#xff1f;下面介绍几款备受好评的相亲软件&#xff0c;帮助你在茫茫人海中找到那个对的人&#xff01; 1、一伴婚恋 这个APP它最大的优点就是信息真实靠谱…

LIMS系统在汽车检测实验室的应用

随着汽车行业的快速发展&#xff0c;汽车检测实验室的工作量不断增加&#xff0c;对实验室的管理效率和数据准确性提出了更高的要求。建设LIMS系统旨在实现实验室的全面数字化管理&#xff0c;提高工作效率&#xff0c;降低运营成本&#xff0c;并提升数据质量与决策支持能力。…

找出可整除性得分最大的整数 - (LeetCode)

概述 昨天非常忙&#xff0c;上午去做体检&#xff0c;下午去拿报告&#xff0c;晚上和老刘吃撸了一个串&#xff0c;深入交流生活的不易&#xff0c;岁月变化的快&#xff0c;话回算法题&#xff0c;今天的题目比较容易&#xff0c;花了一会就能写出来。 今天这题是&#xf…

Filter和Interceptor

会话 cookie 服务器自动将cookie响应给浏览器 浏览器接收响应回来的数据 自动将cookie存储在本地&#xff0c; 后续请求当中浏览器将cookie携带到服务器 cookie不能跨域&#xff0c;存储在客户端 session 存储在服务器 token------>JWT 存储在客户端&#xff0c; 服务…

[leetcode]第 n个丑数

我们把只包含质因子 2、3 和 5 的数称作丑数&#xff08;Ugly Number&#xff09;。求按从小到大的顺序的第 n 个丑数。 示例: 输入: n 10 输出: 12 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。 1 2 3 说明: 1 是丑数。 n 不超过1690。 class Solution {public…

【C++ QT项目实战-03】---- C++ QT系统实现读取JSON文件数据的自动化模式

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a;C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &#…

分布式系统

分布式系统是建立在网络之上的软件系统,其主要特点是具有高度的内聚性和透明性。在一个分布式系统中,一组独立的计算机展现给用户的是一个统一的整体。这些系统拥有多种通用的物理和逻辑资源,可以动态地分配任务,分散的物理和逻辑资源通过计算机网络实现信息交换。 分布式…

Flink 窗口

窗口&#xff08;Window&#xff09; 窗口是处理无限流的核心。 窗口将流分割成有限大小的“桶”&#xff0c;我们可以计算窗口中的数据。 窗口程序一般有键控流&#xff08;keyed streams&#xff09;的窗口程序 和 非键控流&#xff08;non-keyed streams&#xff09;的窗口…

Rabbitmq 搭建使用案例 [附源码]

Rabbitmq 搭建使用案例 文章目录 RabbitMQ搭建docker 代码golang生产者消费者 可视化消费进度 RabbitMQ搭建 docker docker run -d --hostname rabbitmq --name rabbitmq -e RABBITMQ_DEFAULT_USERadmin -e RABBITMQ_DEFAULT_PASSadmin -e RABBITMQ_DEFAULT_VHOSTmy_vhost -e…

LeetCode/NowCoder-复杂度OJ练习

无论你觉得自己多么的不幸&#xff0c;永远有人比你更加不幸。&#x1f493;&#x1f493;&#x1f493; 目录 说在前面 题目一&#xff1a;消失的数字 题目二&#xff1a;轮转数组 SUMUP结尾 说在前面 dear朋友们大家好&#xff01;&#x1f496;&#x1f496;&#x1f496…

线性表(从数据结构的三要素出发)

文章目录 逻辑结构存储结构顺序存储链式存储单链表双链表循环单链表循环双链表静态链表 数据的操作顺序结构链式结构单链表双链表 逻辑结构 线性表是具有相同数据类型的 n ( n ≥ 0 ) n(n≥0) n(n≥0)个数据元素的有限序列&#xff0c;其中 n n n为表长&#xff0c;当 n 0 n0…

力扣:777. 在LR字符串中交换相邻字符

777. 在LR字符串中交换相邻字符 在一个由 L , R 和 X 三个字符组成的字符串&#xff08;例如"RXXLRXRXL"&#xff09;中进行移动操作。一次移动操作指用一个 "LX" 替换一个 "XL"&#xff0c;或者用一个 "XR" 替换一个 "RX"…