java使用Apache POI 操作word文档

news2025/3/26 5:35:48

项目背景:

当我们对一些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 所以处理run
        List<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  空格+正文放到run2
            run1.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);

            //将内容替换到run
            run.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 所以处理run
        List<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.coloradmin.cn/o/2320437.html

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

相关文章

免费开源的NAS解决方案:TrueNAS

TrueNAS是业内知名的FreeNAS系统的升级版&#xff0c;是一款开源的网络存储系统&#xff0c;具有高性能、稳定性和易用性等优点。 TrueNAS目前有三个版本&#xff0c;分别是TrueNAS CORE、TrueNAS ENTERPRISE、TrueNAS SCALE。其中&#xff0c;TrueNAS CORE基于FreeBSD开发&…

LeetCode热题100精讲——Top1:两数之和【哈希】

你好&#xff0c;我是安然无虞。 文章目录 题目背景两数之和C解法Python解法 题目背景 如果大家对于 哈希 类型的概念并不熟悉, 可以先看我之前为此专门写的算法详解: 蓝桥杯算法竞赛系列第九章巧解哈希题&#xff0c;用这3种数据类型足矣 两数之和 题目链接&#xff1a;两数…

Rocky9.5基于sealos快速部署k8s集群

首先需要下载 Sealos 命令行工具&#xff0c;sealos 是一个简单的 Golang 二进制文件&#xff0c;可以安装在大多数 Linux 操作系统中。 以下是一些基本的安装要求&#xff1a; 每个集群节点应该有不同的主机名。主机名不要带下划线。 所有节点的时间需要同步。 需要在 K8s …

阿里云服务器环境部署 四 MySQL主从配置

安装MySQL 导入mysql镜像 docker load -i /opt/dockerinstall/mysql/mysql-8.1.0.tar docker run --privilegedtrue --name mysql8 --restartunless-stopped -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 -v /usr/local/mysql/logs:/var/log/mysql -v /usr/local/mysql/d…

项目日记 -云备份 -服务器配置信息模块

博客主页&#xff1a;【夜泉_ly】 本文专栏&#xff1a;【项目日记-云备份】 欢迎点赞&#x1f44d;收藏⭐关注❤️ 代码已上传 gitee 目录 前言配置信息文件文件配置类getInstance 获得实例readConfigFile 读取配置信息文件 测试 #mermaid-svg-ewlCpjdOf0q0VTLI {font-family:…

Mysql配套测试之查询篇

&#x1f3dd;️专栏&#xff1a;Mysql_猫咪-9527的博客-CSDN博客 &#x1f305;主页&#xff1a;猫咪-9527-CSDN博客 “欲穷千里目&#xff0c;更上一层楼。会当凌绝顶&#xff0c;一览众山小。” 目录 条件查询简单测试&#xff1a; 1.查询英语成绩不及格的同学(<60) 2…

mysql——第二课

学生表 CREATE TABLE student (id int(11) NOT NULL AUTO_INCREMENT,name varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,sex varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,age int(11) DEFAULT NULL,c_id int(10) DEFAULT NULL,PRIMARY KEY (id),KEY c_id (c_id),CONSTR…

Python网络编程入门

一.Socket 简称套接字&#xff0c;是进程之间通信的一个工具&#xff0c;好比现实生活中的插座&#xff0c;所有的家用电器要想工作都是基于插座进行&#xff0c;进程之间要想进行网络通信需要Socket&#xff0c;Socket好比数据的搬运工~ 2个进程之间通过Socket进行相互通讯&a…

arm linux下的读写信号量rw_semphore的实现

本文基于arm linux 5.10来介绍内核中使用的读写信号量rw remphore的实现代码。 内核中信号量结构体struct rw_semaphore的定义在include/linux/rwsem.h 32位architectures下&#xff0c;结构体struct rw_semaphore中的count的使用如下&#xff1a; 先来看信号量的定义和初始化…

C#里使用libxl的数字格式

由于EXCEL里可以表示不同的数字格式, 比如表示货币数字时,与表示普通序号的数字就不一样。 还有科学计算表示的数字使用小数点位数与普通货币也不一样。 如下所示: 要使用这些格式, 下面创建一个例子来演示保存这些数字格式: private void button11_Click(object send…

c#难点整理2

1.对象池的使用 就是先定义一系列的对象&#xff0c;用一个&#xff0c;调一个。 public class ObjectPool<T> where T : new(){private Queue<T> pool; // 用于存储对象的队列private int maxSize; // 对象池的最大容量// 构造函数public ObjectPool(int maxSi…

解锁物联网高效开发,Synaptics SYN43756E Wi-Fi 6E 芯片登场

Synaptics 的 SYN43756E 芯片是一款高性能的 Wi-Fi 6E 支持 11a/b/g/n/ac/ax 的物联网&#xff08;IoT&#xff09;SoC&#xff0c;具备多项先进特性&#xff0c;适用于多种应用场景&#xff0c;以下是其主要优势&#xff1a; 1. 广泛的应用场景 智慧家庭&#xff1a;支持多种…

DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加导出数据功能

前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加导出数据功能📚页面效果📚指令输入�…

2024年河南省职业院校 技能大赛高职组 “大数据分析与应用” 赛项任务书(四)

2024 年河南省职业院校 技能大赛高职组 “大数据分析与应用” 赛项任务书&#xff08;四&#xff09;&#xff09; 背景描述&#xff1a;任务一&#xff1a;Hadoop 完全分布式安装配置&#xff08;25 分&#xff09;任务二&#xff1a;离线数据处理&#xff08;25 分&#xff0…

dify创建第一个Agent

1、首先LLM模型必须支持 Function Calling 由于deepseek-R1本地化部署时还不支持&#xff0c;所以使用 qwq模型。 2、创建空白 Agent 3、为Agent添加工具 4、测试 当未添加时间工具时 询问 时间 如下 5、开启时间工具 询问如下

⭐算法OJ⭐判断二叉搜索树【树的遍历】(C++实现)Validate Binary Search Tree

图论入门【数据结构基础】&#xff1a;什么是树&#xff1f;如何表示树&#xff1f; 之前我们有分别讲解二叉树的三种遍历的相关代码实现&#xff1a; ⭐算法OJ⭐二叉树的前序遍历【树的遍历】&#xff08;C实现&#xff09;Binary Tree Preorder Traversal ⭐算法OJ⭐二叉树的…

2. 商城前端部署

商城客户端前端部署 https://gitee.com/newbee-ltd/newbee-mall-api-go 使用开源新蜂商城的前端&#xff0c;git clone到本地 然后在vscode终端依次输入下列指令&#xff08;配置好vue3相关环境的前提下&#xff09;&#xff1a; npm install npm i --legacy-peer-deps npm …

鸿蒙生态开发

鸿蒙生态开发概述 鸿蒙生态是华为基于开源鸿蒙&#xff08;OpenHarmony&#xff09;构建的分布式操作系统生态&#xff0c;旨在通过开放共享的模式连接智能终端设备、操作系统和应用服务&#xff0c;覆盖消费电子、工业物联网、智能家居等多个领域。以下从定义与架构、核心技术…

基于STM32进行FFT滤波并计算插值DA输出

文章目录 一、前言背景二、项目构思1. 确定FFT点数、采样率、采样点数2. 双缓存设计 三、代码实现1. STM32CubeMX配置和HAL库初始化2. 核心代码 四、效果展示和后话五、项目联想与扩展1. 倍频2. 降频3. 插值3.1 线性插值3.2 样条插值 一、前言背景 STM32 对 AD 采样信号进行快…

【Oracle资源损坏类故障】:详细了解坏块

目录 1、物理坏块与逻辑坏块 1.1、物理坏块 1.2、逻辑坏块 2、两个坏块相关的参数 2.1、db_block_checksum 2.2、db_block_checking 3、检测坏块 3.1、告警日志 3.2、RMAN 3.3、ANALYZE 3.4、数据字典 3.5、DBVERIFY 4、修复坏块 4.1、RMAN修复 4.2、DBMS_REPA…