java使用Apache POI 操作word文檔

項目背景:

當我們對一些word文檔(該文檔包含很多的標題比如 1.1 ,1.2 , 1.2.1.1, 1.2.2.3)當我們刪除其中一項或者幾項時,需要手動的對后續的進行補充。該功能主要是對標題進行自動的補充。

具體步驟:

導入依賴:

<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.2</version></dependency>

官網網址:(覺得麻煩不可也許)

Apache Poi 官方鏈接 可以看官方文檔,其實更方便的可以直接導入依賴后,下載源代碼,直接看源碼的注釋也許

跑一下代碼熟悉一下

首先把下面的代碼復制到編譯器跑一下,看看是否正常運行,順便了解基本使用

package codeByZyc;import org.apache.poi.xwpf.usermodel.*;import java.io.FileInputStream;
import java.io.IOException;public class rederWordTest {public static void main(String[] args) throws IOException {FileInputStream file = new FileInputStream("輸入你的word文檔地址");XWPFDocument document = new XWPFDocument(file);// 獲取word中的段落,無法獲取表格System.out.println("獲取到的段落");for (XWPFParagraph paragraph : document.getParagraphs()) {System.out.println(paragraph.getText());}//  這是只能獲取word中的表格System.out.println("獲取到的表給內容");for (XWPFTable table : document.getTables()) {for (XWPFTableRow row : table.getRows()) {for (XWPFTableCell cell : row.getTableCells()) {System.out.print(cell.getText() + " \t");}System.out.println();}}document.close();file.close();}}

api說明

通過上面的代碼,我們可以知道poi是用過XWPFDocument這個類去獲取的word內容。 下面從段落和表格兩部分進行代碼說明

段落api說明

對于word中的段落他的操作如下:

XWPFDocument(最大的模塊).getParagraphs->Paragraph(負責每一個段落).getRuns->Run(這是最小處理的元素)

下面是進行調式的圖片,配合圖片更好理解:

XWPFDocument:就是最大的那個模塊 信息很大

在這里插入圖片描述

Paragraphs:這是所有的段落集合

在這里插入圖片描述

Paragraph:存放的就是每一段了 里面的runs 是按照格式進行分割的

在這里插入圖片描述

Runs run的集合

具體看下面

Run(最基礎的元素)

這是最重要的那個元素,他是構成所有段落和表格的最小單位。
一個段落他是如何劃分成幾個run的?
他是按照每個字的前后 是否同一個格式(字體,加粗否,大小等)必須完全一樣才能分到一個run里面。具體分割還得調式看

下圖是一些run的切割:
在這里插入圖片描述
這個就是特殊的符號會被劃開
在這里插入圖片描述
這個更離譜 注意 1.3.1后面的空格沒有 ,這個的空格是被劃開的。是因為空格的格式和標題不一樣
在這里插入圖片描述

段落代碼(直接看結尾的整合代碼,寫得更詳細注釋更全面):里面有注釋 應該算比較清楚了 有問題可以下面評論

文件路徑自己填寫一下

package codeByZyc;import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class word {// 匹配數字開頭,小數點隔開 空格后接內容static Pattern headerPattern = Pattern.compile("^(\\d+(?:\\.\\d+)*)(\\s+.*)");//用于計算層級static ArrayList<Integer> headerlist= new ArrayList();//用于存放段落開頭有單個特殊符號的static ArrayList<String> kaitou =new ArrayList<>();// 匹配中文括號列表項(如 (1))
//   static Pattern listItemPattern = Pattern.compile("^((\\d+))(.*)$");public static void main(String[] args) {String path="文件路徑";//初始化計算器for (int i = 0; i < 10; i++) {headerlist.add(0);}//初始化開頭特殊字符kaitou.add("★");kaitou.add("*");//執行代碼updateWord(path);}public static void updateWord(String path){try {// 1. 讀取 .docx 文件FileInputStream fis = new FileInputStream(path);XWPFDocument document = new XWPFDocument(fis);//獲取word每一個段落元素(不包含表格的)List<XWPFParagraph> paragraphs = document.getParagraphs();//  //一個段落一個段落的處理for (int i = 0; i < paragraphs.size(); i++) {wordDuanluo(paragraphs.get(i));}//開始村存回去FileOutputStream out = new FileOutputStream("文件路徑代碼生成.docx");document.write(out);// 4. 關閉流document.close();fis.close();out.close();} catch (Exception e) {e.printStackTrace();}}//處理段落private static void wordDuanluo(XWPFParagraph paragraph) {//每個段落下面還有更小的元素,叫run 所以處理runList<XWPFRun> runs = paragraph.getRuns();//直接匹配第一個 是那就是標題 不是那就是正文if (runs.size()==1){wordRun(runs.get(0));}else if (runs.size()==2){//只有兩個識別開頭是不是包含特殊  時走一個通道 不是走二通道//標志是否匹配boolean flagkaitou= false;for (int i = 0; i < kaitou.size(); i++) {if (kaitou.get(i).equals(runs.get(0).text())){flagkaitou=true;break;}}if (flagkaitou==true){//證明開頭匹配走一同到wordRun(runs.get(1));}else {//不匹配wordRun(runs.get(0),runs.get(1));}}else if (runs.size()>2){//數量在三個及以上//標志是否匹配boolean flagkaitou= false;for (int i = 0; i < kaitou.size(); i++) {if (kaitou.get(i).equals(runs.get(0).text())){flagkaitou=true;break;}}if (flagkaitou==true){//證明開頭匹配wordRun(runs.get(1),runs.get(2));}else {//不匹配wordRun(runs.get(0),runs.get(1));}}}// 處理非常特殊的 標題后面跟的空格字體和標題不一樣分開了 進行拼接private static void wordRun(XWPFRun run1, XWPFRun run2) {//run是作為操作word的非常小的元素了,他會把每個段落換分成幾個run組成  具體劃分規則我也不知道 (我看案列 是按格式進行 格式一樣的情況大概率是在一起)//采用正則表達式進行匹配Matcher matcher = headerPattern.matcher(run1.text()+run2.text());if (matcher.find()) {//匹配成功//保存序號后面的文章用于拼接String contex = matcher.group(2);//按照.進行切割String[] originalParts = matcher.group(1).split("\\.");//根據長度判斷層級 一個就一級int length = originalParts.length;//文檔按照順序1  1.1  1.1.1//將子層級覆蓋掉for (int i = length; i < headerlist.size(); i++) {headerlist.set(i,0);}headerlist.set(length - 1, (headerlist.get(length - 1) + 1));StringBuffer result = new StringBuffer();//拼接正確的序號for (int i = 0; i < length; i++) {result.append(headerlist.get(i));result.append(".");}//多出一個. 進行刪除result.deleteCharAt(result.length()-1);//序號放到run1  空格+正文放到run2run1.setText(result.toString(),0);run2.setText(contex,0);}}private static void wordRun(XWPFRun run) {//run是作為操作word的非常小的元素了,他會把每個段落換分成幾個run組成  具體劃分規則我也不知道 (我看案列 是按格式進行 格式一樣的情況大概率是在一起)//采用正則表達式進行匹配Matcher matcher = headerPattern.matcher(run.text());if (matcher.find()) {//匹配成功//保存序號后面的文章用于拼接String contex = matcher.group(2);//按照.進行切割String[] originalParts = matcher.group(1).split("\\.");//根據長度判斷層級 一個就一級int length = originalParts.length;//文檔按照順序1  1.1  1.1.1//將子層級覆蓋掉for (int i = length; i < headerlist.size(); i++) {headerlist.set(i,0);}headerlist.set(length - 1, (headerlist.get(length - 1) + 1));StringBuffer result = new StringBuffer();//拼接正確的序號for (int i = 0; i < length; i++) {result.append(headerlist.get(i));result.append(".");}//多出一個. 進行刪除result.deleteCharAt(result.length()-1);result.append(contex);//將內容替換到runrun.setText(result.toString(),0);}}}
表格api說明 基本上和段落一樣 有一點點不一樣
XWPFDocument(最大的模塊).getTables->XWPFTable(負責每一個表格).getRows->Row(代表一行).getTableCells->XWPFTableCell(每一格子)[由于cell無法更改具體看下圖] 需要深入到 run

注意,我原來以為在cell就可以更改他的文本
在這里插入圖片描述
但是看源碼可以知道 他是在尾部追加并不是覆蓋,所以還是只能追到run去覆蓋。

表格代碼:(直接看結尾的整合代碼,寫得更詳細注釋更全面)
import org.apache.poi.xwpf.usermodel.*;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class TestWord {// 匹配表格的 結尾開頭是數字 空格幾個都行static  Pattern tablePattern = Pattern.compile("^(\\d+)(\\s*)$");static  Integer tableCount=0;//用于存放段落開頭有單個特殊符號的static ArrayList<String> kaitou =new ArrayList<>();public static void main(String[] args) {String path="測試table.docx";//初始化開頭特殊字符kaitou.add("★");kaitou.add("*");readWord(path);}public static void readWord(String filePath){try {// 1. 讀取 .docx 文件FileInputStream fis = new FileInputStream(filePath);XWPFDocument document = new XWPFDocument(fis);//直接一層一層找一下找到 run  試過在cell進行修改 但是cell的修改是 原來的基礎上進行了新的增加 不能進行替換 直接找到run進行替換for (XWPFTable table : document.getTables()) {//一個table 清除一次計數器tableCount=0;for (XWPFTableRow row : table.getRows()) {for (XWPFTableCell cell : row.getTableCells()) {for (XWPFParagraph paragraph : cell.getParagraphs()) {List<XWPFRun> runs = paragraph.getRuns();//直接匹配第一行if (runs.size()==1){tableup(runs.get(0));}else if (runs.size()>1){//這邊就是 考慮到前面有個特殊符號的情況 就需要判斷了XWPFRun run1 = runs.get(0);//第一個不是特殊那就直接走第一通道boolean flag=false;for (int i = 0; i < kaitou.size(); i++) {if (kaitou.get(i).equals(run1.text())){flag=true;break;}}if (flag){tableup(runs.get(1));}else {tableup(run1);}}}}}}System.out.println("測試完成");FileOutputStream out = new FileOutputStream("生成的table.docx");document.write(out);// 4. 關閉流document.close();fis.close();out.close();} catch (Exception e) {e.printStackTrace();}}public  static void  tableup(XWPFRun run){Matcher matcher = tablePattern.matcher(run.text());if (matcher.find()){//匹配成功  開始更換層級String content = matcher.group(2);String originalParts = matcher.group(1);//開始覆蓋掉序號 并拼接后面的內容tableCount++;StringBuffer result=new StringBuffer();result.append(tableCount+content);//寫入run.setText(result.toString(),0);}}
}

總結

整合代碼:

package codeByZyc;import org.apache.poi.xwpf.usermodel.*;import java.io.FileInputStream;import java.io.FileOutputStream;import java.util.ArrayList;import java.util.List;import java.util.regex.Matcher;import java.util.regex.Pattern;public class Upword {// 匹配數字開頭,小數點隔開 空格后接內容static Pattern headerPattern = Pattern.compile("^(\\D?)(\\d+(?:\\.\\d+)*)(\\s+)");//用于段落計算層級static ArrayList<Integer> headerlist= new ArrayList();//用于存放段落開頭有單個特殊符號的// 匹配表格的 結尾開頭是數字 空格幾個都行static  Pattern tablePattern = Pattern.compile("^(\\D?)(\\d+)(\\s*)$");//匹配表格的層級static  Integer tableCount=0;// 匹配中文括號列表項(如 (1))
//   static Pattern listItemPattern = Pattern.compile("^((\\d+))(.*)$");public static void main(String[] args) {String path="測試專用刪除部分標題.docx";//初始化段落層級計算器for (int i = 0; i < 10; i++) {headerlist.add(0);}//執行代碼updateWord(path);}public static void updateWord(String path){try {// 1. 讀取 .docx 文件FileInputStream fis = new FileInputStream(path);XWPFDocument document = new XWPFDocument(fis);//獲取word每一個段落元素(不包含表格的)List<XWPFParagraph> paragraphs = document.getParagraphs();//  //一個段落一個段落的處理for (int i = 0; i < paragraphs.size(); i++) {wordDuanluo(paragraphs.get(i));}//獲取word的表格
//            直接一層一層找一下找到 run  試過在cell進行修改 但是cell的修改是 原來的基礎上進行了新的增加 不能進行替換 直接找到run進行替換for (XWPFTable table : document.getTables()) {wordtable(table);}//開始村存回去FileOutputStream out = new FileOutputStream("代碼生成.docx");document.write(out);// 4. 關閉流document.close();fis.close();out.close();} catch (Exception e) {e.printStackTrace();}}//處理表格private static void wordtable(XWPFTable table){//一個table 清除一次計數器tableCount=0;for (XWPFTableRow row : table.getRows()) {for (XWPFTableCell cell : row.getTableCells()) {for (XWPFParagraph paragraph : cell.getParagraphs()) {List<XWPFRun> runs = paragraph.getRuns();//直接匹配第一行if (runs.size()==1){wordTableRun(runs.get(0));}else if (runs.size()==2){wordTableRun(runs.get(0),runs.get(1));}}}}}//處理段落private static void wordDuanluo(XWPFParagraph paragraph) {//每個段落下面還有更小的元素,叫run 所以處理runList<XWPFRun> runs = paragraph.getRuns();/* 這里需要注意一下因為我的測試文檔的需求標題是這樣的 *1.2.1 背景展望   像這個 要是格式不一樣會被劃分成 三個部分  *    1.2.1   還有一個空格  。 但是要是格式一樣 就直接化成一個了主要是我的測試文檔是有些一樣 有些不一樣所以需要考慮的比較多。*/if (runs.size()==1){//針對只劃分一個的  那沒得說 直接走第一個wordRun(runs.get(0));}else if (runs.size()==2){//這就是兩個 那可能是兩種情況:  *1.2.1  空格   或者 *   1.2.1空格   就是兩種了wordRun(runs.get(0),runs.get(1));}else if (runs.size()>2){//數量在三個及以上//這種 就是我上面說的 三個格式都不一樣wordRun(runs.get(0),runs.get(1),runs.get(2));}}private static void wordRun(XWPFRun run) {//run是作為操作word的非常小的元素了,他會把每個段落換分成幾個run組成  具體劃分規則我也不知道 (我看案列 是按格式進行 格式一樣的情況大概率是在一起)//采用正則表達式進行匹配Matcher matcher = headerPattern.matcher(run.text());if (matcher.find()) {//匹配成功String  oldxuaho=matcher.group(2);//按照.進行切割String[] originalParts =oldxuaho.split("\\.");//根據長度判斷層級 一個就一級int length = originalParts.length;//文檔按照順序1  1.1  1.1.1//將子層級覆蓋掉for (int i = length; i < headerlist.size(); i++) {headerlist.set(i,0);}headerlist.set(length - 1, (headerlist.get(length - 1) + 1));StringBuffer result = new StringBuffer();//拼接正確的序號//把序號前面的放進來result.append(matcher.group(1));for (int i = 0; i < length; i++) {result.append(headerlist.get(i));result.append(".");}//多出一個. 進行刪除result.deleteCharAt(result.length()-1);//替換if(run.text().contains(oldxuaho)){//進行替換String s = run.text();s.replace(oldxuaho,result.toString());run.setText(s,0);}}}// 處理非常特殊的 標題后面跟的空格字體和標題不一樣分開了 進行拼接private static void wordRun(XWPFRun run1, XWPFRun run2) {//run是作為操作word的非常小的元素了,他會把每個段落換分成幾個run組成  具體劃分規則我也不知道 (我看案列 是按格式進行 格式一樣的情況大概率是在一起)//采用正則表達式進行匹配Matcher matcher = headerPattern.matcher(run1.text()+run2.text());if (matcher.find()) {//匹配成功String  oldxuaho=matcher.group(2);//按照.進行切割String[] originalParts =oldxuaho.split("\\.");//根據長度判斷層級 一個就一級int length = originalParts.length;//文檔按照順序1  1.1  1.1.1//將子層級覆蓋掉for (int i = length; i < headerlist.size(); i++) {headerlist.set(i,0);}headerlist.set(length - 1, (headerlist.get(length - 1) + 1));StringBuffer result = new StringBuffer();//把前面的放進來result.append(matcher.group(1));//拼接正確的序號for (int i = 0; i < length; i++) {result.append(headerlist.get(i));result.append(".");}//多出一個. 進行刪除result.deleteCharAt(result.length()-1);//查找替換 具體思路看一看下面的if(run1.text().contains(oldxuaho)){//進行替換String s = run1.text();s.replace(oldxuaho,result.toString());run1.setText(s,0);}else if (run2.text().contains(oldxuaho)){String s = run2.text();s.replace(oldxuaho,result.toString());run2.setText(s,0);}}}private static void wordRun(XWPFRun run1, XWPFRun run2,XWPFRun run3) {//run是作為操作word的非常小的元素了,他會把每個段落換分成幾個run組成  具體劃分規則我也不知道 (我看案列 是按格式進行 格式一樣的情況大概率是在一起)//采用正則表達式進行匹配Matcher matcher = headerPattern.matcher(run1.text()+run2.text()+run3.text());if (matcher.find()) {//匹配成功String oldxuaho=matcher.group(2);//按照.進行切割String[] originalParts = oldxuaho.split("\\.");//根據長度判斷層級 一個就一級int length = originalParts.length;//文檔按照順序1  1.1  1.1.1//將子層級覆蓋掉for (int i = length; i < headerlist.size(); i++) {headerlist.set(i,0);}headerlist.set(length - 1, (headerlist.get(length - 1) + 1));StringBuffer result = new StringBuffer();//直接將他們拼起來result.append(matcher.group(1));//拼接正確的序號for (int i = 0; i < length; i++) {result.append(headerlist.get(i));result.append(".");}//多出一個. 進行刪除result.deleteCharAt(result.length()-1);/*下面就需要考慮 run1 2 3 如何劃分了因為我的matcher.group 把他們三個合并的 拆分成了 三部分  (數字標題前面的部分) (數字標題) 空格內容(內容可能有可能無)* 情況1:  最開始說到的  *       1.2.1    空格內容(內容可能有可能無)* 情況2:  也有可能是: *1.2.1   空格內容(內容可能有可能無)     內容情況3:  也有可能是: *      1.2.1空格內容(內容可能有可能無)   內容情況4:  *1.2.1空格內容(內容可能有可能無)  內容  內容解決思路: group(2)匹配的序號 只需要 挨個便利 看看 哪個run包含 進行替換就行其他的保持不變。* *///查找if(run1.text().contains(oldxuaho)){//進行替換String s = run1.text();s.replace(oldxuaho,result.toString());run1.setText(s,0);}else if (run2.text().contains(oldxuaho)){String s = run2.text();s.replace(oldxuaho,result.toString());run2.setText(s,0);}else {String s = run3.text();s.replace(oldxuaho,result.toString());run3.setText(s,0);}}}//處理表格的單元格private static void wordTableRun(XWPFRun run){Matcher matcher = tablePattern.matcher(run.text());if (matcher.find()){//匹配成功  開始更換層級String oldxuhao  = matcher.group(2);//開始覆蓋掉序號 并拼接后面的內容tableCount++;String s = run.text();s.replace(oldxuhao,tableCount.toString());//寫入run.setText(s,0);}}private static void wordTableRun(XWPFRun run1,XWPFRun run2){Matcher matcher = tablePattern.matcher(run1.text()+run2.text());if (matcher.find()){String oldxuhao=matcher.group(2);//開始覆蓋掉序號 并拼接后面的內容tableCount++;String s;if (run1.text().contains(oldxuhao)){s=run1.text();s.replace(oldxuhao,tableCount.toString());run1.setText(s,0);}else if (run2.text().contains(oldxuhao)){s=run2.text();s.replace(oldxuhao,tableCount.toString());run2.setText(s,0);}}}}

難點:

我感覺最大的難點就是對Api的熟悉,需要看看源碼或者文檔。以及利用正則表達式對標題進行匹配

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/74096.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/74096.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/74096.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

接收與發送ipv6數據包

一、ipv6的概念 IPv6 是英文 “Internet Protocol Version 6”&#xff08;互聯網協議第 6 版&#xff09;的縮寫&#xff0c;是互聯網工程任務組&#xff08;IETF&#xff09;設計的用于替代 IPv4 的下一代 IP 協議&#xff0c;其地址數量號稱可以為全世界的每一粒沙子編上…

龍虎榜——20250321

今日A股龍虎榜方向分析 根據2025年3月21日龍虎榜數據&#xff08;漲停56家&#xff0c;跌停31家&#xff09;&#xff0c;市場呈現結構性分化行情&#xff0c;資金聚焦海洋經濟、機器人、鋰電等主線&#xff0c;部分個股遭機構大幅拋售。以下是具體方向解析&#xff1a; 一、資…

springboot milvus search向量相似度查詢 踩坑使用經驗

1.前提提要&#xff1a;java的pom 版本為&#xff1a;2.4.9 milvus 版本是&#xff1a;2.4.13-hotfix 2.先來工具類方法 /*** 向量搜索* param client* param query* return*/public SearchResp search(NonNull MilvusClientV2 client, NonNull VectorCondition query) {final …

[網絡安全] 濫用Azure內置Contributor角色橫向移動至Azure VM

本文來源于團隊的超輝老師&#xff0c;其系統分析了Azure RBAC角色模型及其在權限濫用場景下的攻擊路徑。通過利用AADInternals工具提升用戶至Contributor角色&#xff0c;攻擊者可在Azure VM中遠程執行命令&#xff0c;創建后門賬戶&#xff0c;實現橫向移動。文中詳述了攻擊步…

Android Compose 基礎布局之 Box 和 Stack 源碼深度剖析(九)

Android Compose 基礎布局之 Box 和 Stack 源碼深度剖析 一、引言 1.1 Android 開發中布局的重要性 在 Android 應用開發里&#xff0c;布局是構建用戶界面&#xff08;UI&#xff09;的關鍵環節。良好的布局設計能夠提升用戶體驗&#xff0c;使應用界面更加美觀、易用且具有…

知識蒸餾:讓大模型“瘦身“而不失智慧的魔術

引言&#xff1a;當AI模型需要"減肥" 在人工智能領域&#xff0c;一個有趣的悖論正在上演&#xff1a;大模型的參數規模每年以10倍速度增長&#xff0c;而移動設備的算力卻始終受限。GPT-4的1750億參數需要價值500萬美元的GPU集群運行&#xff0c;但現實中的智能設備…

多路FM調頻廣播解調器:多路電臺FM廣播信號一體化解調處理方案

多路FM調頻廣播解調器&#xff1a;多路電臺FM廣播信號一體化解調處理方案 支持OEM型號開放式協議支持二次開發設計 北京海特偉業科技有限公司任洪卓發布于2025年3月21日 在信息傳播領域&#xff0c;FM調頻廣播媒體以其獨特的優勢持續發揮著重要作用。為了應對日益增長的多路…

如何在Spring Boot中設置HttpOnly Cookie以增強安全性

引言 在Web開發中,Cookie是用于在客戶端和服務器之間傳遞信息的重要機制。然而,Cookie的安全性一直是一個備受關注的問題。特別是當Cookie中存儲了敏感信息(如會話ID)時,如何防止這些信息被惡意腳本竊取就顯得尤為重要。HttpOnly屬性是增強Cookie安全性的一種有效手段。本…

LangManus:新一代開源智能體框架如何讓AI開發更簡單?

你是否想過&#xff0c;代碼生成、數據分析甚至系統調試&#xff0c;都能由一個“AI助手”自動完成&#xff1f;最近&#xff0c;一款名為LangManus的開源項目在開發者社區掀起熱議。它不只是一個工具庫&#xff0c;更是一個能自主思考、執行復雜任務的智能體框架。無論是企業內…

【STM32】SPI通信協議W25Q64Flash存儲器芯片(學習筆記)

通信接口部分有介紹SPI&#xff1a;【STM32】USART串口協議&串口外設-學習筆記-CSDN博客 SPI通信協議 SPI通信 SPI&#xff08;Serial Peripheral Interface&#xff09;是由Motorola公司開發的一種通用數據總線四根通信線&#xff1a;SCK&#xff08;Serial Clock&…

批量合并 PPT 文件,支持合并成單個文件也支持按文件夾合并

合并多個 PPT 為一個 PPT 文檔是我們經常會碰到的需求&#xff0c;合并后不僅更容易管理&#xff0c;在某些場景&#xff08;比如批量打印&#xff09;下也非常的有用&#xff0c;那當我們需要批量合并多個 PPT 文檔地時候&#xff0c;我們有沒有比較高效的方法呢&#xff1f;今…

LDAP從入門到實戰:環境部署與配置指南(下)

#作者&#xff1a;朱雷 接上篇&#xff1a;《LDAP從入門到實戰&#xff1a;環境部署與配置指南&#xff08;上&#xff09;》 鏈接: link 文章目錄 2.5.添加賬號2.6.停止服務2.7.使用TLS證書2.7.1. TLS 證書2.7.2. TLS 配置2.7.3. 服務器配置 2.8.使用安全連接的反向代理 2.5…

發現一個好用的Vue.js內置組件

目錄 一、這個好用的內置組件是什么&#xff1f; 二、這個組件的主要功能 三、怎么使用&#xff1f; 四、使用注意事項 五、我的使用場景 一、這個好用的內置組件是什么&#xff1f; 今天在優化我的平臺應用時&#xff0c;發現一個好用的組件標簽--<keep-alive>。 …

dart學習記錄5(類、對象)

1.獲取運行時對象類型 使用Object 屬性的 runtimeType&#xff0c;它返回一個 Type 對象。 print(a 的類型是 ${a.runtimeType});??警告 在測試對象的類型時建議使用object is Type比測試 object.runtimeType Type 更穩定。 2.實例變量的聲明 class Point {double? x;…

啟明星辰春招面試題

《網安面試指南》https://mp.weixin.qq.com/s/RIVYDmxI9g_TgGrpbdDKtA?token1860256701&langzh_CN 5000篇網安資料庫https://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247486065&idx2&snb30ade8200e842743339d428f414475e&chksmc0e4732df793fa3bf39…

Live555+Windows+MSys2 編譯Androidso庫和運行使用

下載 wget http://www.live555.com/liveMedia/public/live555-latest.tar.gz tar -xzvf live555-latest.tar.gz加入版本控制 git init git add . git commit -a -m "first init" git log修改config.android-arm64 cd live vim config.android-arm64 ./genMakefile…

實用工具-Stirling-PDF

windows桌面版參考這個文檔 Getting Started | Stirling-PDF 安裝包推薦使用迅雷下載&#xff0c;先轉存到迅雷網盤在使用迅雷下載速度嘎嘎快。 github:https://github.com/Stirling-Tools/Stirling-PDF Stirling-PDF 是一個強大的、基于 Web 的開源 PDF 處理工具&#xff0c…

借助AI Agent實現數據分析

在當今數據驅動的世界中&#xff0c;數據分析已成為企業決策、科學研究和社會治理的核心工具。然而&#xff0c;隨著數據量的爆炸式增長和復雜性的提升&#xff0c;傳統的數據分析方法面臨著效率低下、成本高昂和人力不足等挑戰。AI技術的快速發展&#xff0c;尤其是AI Agent的…

JavaScript實現一個函數,將數組扁平化(flatten),即把多維數組轉為一維數組。

大白話實現一個函數&#xff0c;將數組扁平化&#xff08;flatten&#xff09;&#xff0c;即把多維數組轉為一維數組。 思路 實現數組扁平化的基本思路是遍歷數組中的每個元素&#xff0c;如果元素是數組&#xff0c;就遞歸地將其扁平化并添加到結果數組中&#xff1b;如果元…

麒麟操作系統安裝人大金倉數據庫

如果你想擁有你從未擁有過的東西&#xff0c;那么你必須去做你從未做過的事情 在當前數字化轉型和信息安全備受重視的背景下&#xff0c;眾多公司積極推進國產化改造進程。在操作系統領域&#xff0c;統信、open 歐拉、中標麒麟、銀河麒麟等國產操作系統嶄露頭角&#xff0c;逐…