|
|
@@ -0,0 +1,533 @@
|
|
|
+package com.hotent.util.wordexcelutils;/**
|
|
|
+ * @program: cbjs-mvue-master
|
|
|
+ * @description:
|
|
|
+ * @author: zhao yue yue
|
|
|
+ * @create: 2026-01-27 15:07
|
|
|
+ */
|
|
|
+import org.apache.poi.xwpf.usermodel.*;
|
|
|
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
|
|
|
+
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Iterator;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+/**
|
|
|
+ *@author: zhao yue yue
|
|
|
+ *@create: 2026-01-27 15:07
|
|
|
+ */
|
|
|
+public class WordDocumentUtils {
|
|
|
+ /**
|
|
|
+ * 删除文档中所有指定的文本
|
|
|
+ * @param document Word文档对象
|
|
|
+ * @param textToDelete 要删除的文本
|
|
|
+ * @return 是否删除了文本
|
|
|
+ */
|
|
|
+ public static boolean deleteAllText(XWPFDocument document, String textToDelete) {
|
|
|
+ if (document == null || textToDelete == null || textToDelete.trim().isEmpty()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean textDeleted = false;
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 1. 处理段落中的文本
|
|
|
+ textDeleted = deleteTextFromParagraphs(document, textToDelete) || textDeleted;
|
|
|
+
|
|
|
+ // 2. 处理表格中的文本
|
|
|
+ textDeleted = deleteTextFromTables(document, textToDelete) || textDeleted;
|
|
|
+
|
|
|
+ // 3. 处理页眉中的文本
|
|
|
+ textDeleted = deleteTextFromHeaders(document, textToDelete) || textDeleted;
|
|
|
+
|
|
|
+ // 4. 处理页脚中的文本
|
|
|
+ textDeleted = deleteTextFromFooters(document, textToDelete) || textDeleted;
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+ return textDeleted;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从段落中删除文本
|
|
|
+ */
|
|
|
+ private static boolean deleteTextFromParagraphs(XWPFDocument document, String textToDelete) {
|
|
|
+ boolean textDeleted = false;
|
|
|
+
|
|
|
+ for (XWPFParagraph paragraph : document.getParagraphs()) {
|
|
|
+ if (deleteTextFromParagraph(paragraph, textToDelete)) {
|
|
|
+ textDeleted = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return textDeleted;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从单个段落中删除文本
|
|
|
+ */
|
|
|
+ private static boolean deleteTextFromParagraph(XWPFParagraph paragraph, String textToDelete) {
|
|
|
+ if (paragraph == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean textDeleted = false;
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 获取段落的所有Run
|
|
|
+ List<XWPFRun> runs = paragraph.getRuns();
|
|
|
+ if (runs.isEmpty()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 方法1:直接使用XML处理(最可靠)
|
|
|
+ String paragraphXml = paragraph.getCTP().xmlText();
|
|
|
+ if (paragraphXml.contains(textToDelete)) {
|
|
|
+ // 替换文本
|
|
|
+ String newXml = paragraphXml.replace(textToDelete, "");
|
|
|
+ // 使用正确的方式重建段落
|
|
|
+ try {
|
|
|
+ CTP newCtp = CTP.Factory.parse(newXml);
|
|
|
+ paragraph.getCTP().set(newCtp);
|
|
|
+ textDeleted = true;
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 如果XML解析失败,使用其他方法
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 方法2:遍历Run逐个处理
|
|
|
+ if (!textDeleted) {
|
|
|
+ textDeleted = processRunsForDeletion(runs, textToDelete);
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+ return textDeleted;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理Run以删除文本
|
|
|
+ */
|
|
|
+ private static boolean processRunsForDeletion(List<XWPFRun> runs, String textToDelete) {
|
|
|
+ boolean textDeleted = false;
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 先收集所有Run的文本
|
|
|
+ List<String> runTexts = new ArrayList<>();
|
|
|
+ List<XWPFRun> runList = new ArrayList<>(runs);
|
|
|
+
|
|
|
+ for (XWPFRun run : runList) {
|
|
|
+ String text = run.getText(0);
|
|
|
+ runTexts.add(text != null ? text : "");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 合并所有Run的文本
|
|
|
+ String combinedText = String.join("", runTexts);
|
|
|
+
|
|
|
+ if (combinedText.contains(textToDelete)) {
|
|
|
+ // 找到要删除的文本位置
|
|
|
+ int start = combinedText.indexOf(textToDelete);
|
|
|
+ int end = start + textToDelete.length();
|
|
|
+
|
|
|
+ // 计算文本在哪些Run中
|
|
|
+ int currentPos = 0;
|
|
|
+ for (int i = 0; i < runList.size(); i++) {
|
|
|
+ String runText = runTexts.get(i);
|
|
|
+ if (runText == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ int runStart = currentPos;
|
|
|
+ int runEnd = currentPos + runText.length();
|
|
|
+
|
|
|
+ // 检查这个Run是否包含要删除的文本
|
|
|
+ if (runEnd > start && runStart < end) {
|
|
|
+ // 计算在这个Run中要删除的字符范围
|
|
|
+ int deleteStart = Math.max(0, start - runStart);
|
|
|
+ int deleteEnd = Math.min(runText.length(), end - runStart);
|
|
|
+
|
|
|
+ // 删除文本
|
|
|
+ String newText = runText.substring(0, deleteStart) +
|
|
|
+ runText.substring(deleteEnd);
|
|
|
+
|
|
|
+ // 更新Run的文本
|
|
|
+ runList.get(i).setText(newText, 0);
|
|
|
+ textDeleted = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ currentPos += runText.length();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+ return textDeleted;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从表格中删除文本
|
|
|
+ */
|
|
|
+ private static boolean deleteTextFromTables(XWPFDocument document, String textToDelete) {
|
|
|
+ boolean textDeleted = false;
|
|
|
+
|
|
|
+ for (XWPFTable table : document.getTables()) {
|
|
|
+ for (XWPFTableRow row : table.getRows()) {
|
|
|
+ for (XWPFTableCell cell : row.getTableCells()) {
|
|
|
+ for (XWPFParagraph paragraph : cell.getParagraphs()) {
|
|
|
+ if (deleteTextFromParagraph(paragraph, textToDelete)) {
|
|
|
+ textDeleted = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return textDeleted;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从页眉中删除文本
|
|
|
+ */
|
|
|
+ private static boolean deleteTextFromHeaders(XWPFDocument document, String textToDelete) {
|
|
|
+ boolean textDeleted = false;
|
|
|
+
|
|
|
+ try {
|
|
|
+ List<XWPFHeader> headers = document.getHeaderList();
|
|
|
+ for (XWPFHeader header : headers) {
|
|
|
+ for (XWPFParagraph paragraph : header.getParagraphs()) {
|
|
|
+ if (deleteTextFromParagraph(paragraph, textToDelete)) {
|
|
|
+ textDeleted = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理页眉中的表格
|
|
|
+ for (XWPFTable table : header.getTables()) {
|
|
|
+ for (XWPFTableRow row : table.getRows()) {
|
|
|
+ for (XWPFTableCell cell : row.getTableCells()) {
|
|
|
+ for (XWPFParagraph paragraph : cell.getParagraphs()) {
|
|
|
+ if (deleteTextFromParagraph(paragraph, textToDelete)) {
|
|
|
+ textDeleted = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+ return textDeleted;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从页脚中删除文本
|
|
|
+ */
|
|
|
+ private static boolean deleteTextFromFooters(XWPFDocument document, String textToDelete) {
|
|
|
+ boolean textDeleted = false;
|
|
|
+
|
|
|
+ try {
|
|
|
+ List<XWPFFooter> footers = document.getFooterList();
|
|
|
+ for (XWPFFooter footer : footers) {
|
|
|
+ for (XWPFParagraph paragraph : footer.getParagraphs()) {
|
|
|
+ if (deleteTextFromParagraph(paragraph, textToDelete)) {
|
|
|
+ textDeleted = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理页脚中的表格
|
|
|
+ for (XWPFTable table : footer.getTables()) {
|
|
|
+ for (XWPFTableRow row : table.getRows()) {
|
|
|
+ for (XWPFTableCell cell : row.getTableCells()) {
|
|
|
+ for (XWPFParagraph paragraph : cell.getParagraphs()) {
|
|
|
+ if (deleteTextFromParagraph(paragraph, textToDelete)) {
|
|
|
+ textDeleted = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+ return textDeleted;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 强力删除方法:完全重建段落
|
|
|
+ */
|
|
|
+ public static void forceDeleteText(XWPFDocument document, String textToDelete) {
|
|
|
+ if (document == null || textToDelete == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 处理所有段落
|
|
|
+ for (XWPFParagraph paragraph : document.getParagraphs()) {
|
|
|
+ forceDeleteFromParagraph(paragraph, textToDelete);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理所有表格
|
|
|
+ for (XWPFTable table : document.getTables()) {
|
|
|
+ for (XWPFTableRow row : table.getRows()) {
|
|
|
+ for (XWPFTableCell cell : row.getTableCells()) {
|
|
|
+ for (XWPFParagraph paragraph : cell.getParagraphs()) {
|
|
|
+ forceDeleteFromParagraph(paragraph, textToDelete);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 强力删除段落中的文本
|
|
|
+ */
|
|
|
+ private static void forceDeleteFromParagraph(XWPFParagraph paragraph, String textToDelete) {
|
|
|
+ try {
|
|
|
+ String paragraphText = paragraph.getText();
|
|
|
+ if (paragraphText == null || !paragraphText.contains(textToDelete)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 完全重建段落
|
|
|
+ String newText = paragraphText.replace(textToDelete, "");
|
|
|
+
|
|
|
+ // 清空段落
|
|
|
+ List<XWPFRun> runs = paragraph.getRuns();
|
|
|
+ for (int i = runs.size() - 1; i >= 0; i--) {
|
|
|
+ try {
|
|
|
+ paragraph.removeRun(i);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 忽略异常
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重新添加文本
|
|
|
+ if (!newText.trim().isEmpty()) {
|
|
|
+ XWPFRun run = paragraph.createRun();
|
|
|
+ run.setText(newText);
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 专门删除占位符的方法 - 这是最简单最可靠的方法
|
|
|
+ */
|
|
|
+ public static void deletePlaceholder(XWPFDocument document, String placeholder) {
|
|
|
+ if (document == null || placeholder == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理所有段落
|
|
|
+ for (XWPFParagraph paragraph : document.getParagraphs()) {
|
|
|
+ String text = paragraph.getText();
|
|
|
+ if (text != null && text.contains(placeholder)) {
|
|
|
+ // 清空段落所有内容
|
|
|
+ List<XWPFRun> runs = paragraph.getRuns();
|
|
|
+ for (int i = runs.size() - 1; i >= 0; i--) {
|
|
|
+ try {
|
|
|
+ paragraph.removeRun(i);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 忽略异常
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 可选:也可以删除整个段落
|
|
|
+ // document.removeBodyElement(document.getPosOfParagraph(paragraph));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理表格
|
|
|
+ for (XWPFTable table : document.getTables()) {
|
|
|
+ for (XWPFTableRow row : table.getRows()) {
|
|
|
+ for (XWPFTableCell cell : row.getTableCells()) {
|
|
|
+ for (XWPFParagraph paragraph : cell.getParagraphs()) {
|
|
|
+ String text = paragraph.getText();
|
|
|
+ if (text != null && text.contains(placeholder)) {
|
|
|
+ // 清空段落所有内容
|
|
|
+ List<XWPFRun> runs = paragraph.getRuns();
|
|
|
+ for (int i = runs.size() - 1; i >= 0; i--) {
|
|
|
+ try {
|
|
|
+ paragraph.removeRun(i);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 忽略异常
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查找并删除占位符段落 - 这个方法更彻底
|
|
|
+ */
|
|
|
+ public static void deletePlaceholderParagraph(XWPFDocument document, String placeholderText) {
|
|
|
+ if (document == null || placeholderText == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查找包含占位符的段落
|
|
|
+ List<XWPFParagraph> paragraphsToDelete = new ArrayList<>();
|
|
|
+
|
|
|
+ // 检查普通段落
|
|
|
+ for (XWPFParagraph paragraph : document.getParagraphs()) {
|
|
|
+ String text = paragraph.getText();
|
|
|
+ if (text != null && text.contains(placeholderText)) {
|
|
|
+ paragraphsToDelete.add(paragraph);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查表格中的段落
|
|
|
+ for (XWPFTable table : document.getTables()) {
|
|
|
+ for (XWPFTableRow row : table.getRows()) {
|
|
|
+ for (XWPFTableCell cell : row.getTableCells()) {
|
|
|
+ for (XWPFParagraph paragraph : cell.getParagraphs()) {
|
|
|
+ String text = paragraph.getText();
|
|
|
+ if (text != null && text.contains(placeholderText)) {
|
|
|
+ paragraphsToDelete.add(paragraph);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 删除找到的段落(从文档中移除)
|
|
|
+ for (XWPFParagraph paragraph : paragraphsToDelete) {
|
|
|
+ try {
|
|
|
+ // 获取段落位置并移除
|
|
|
+ int pos = document.getPosOfParagraph(paragraph);
|
|
|
+ if (pos >= 0) {
|
|
|
+ document.removeBodyElement(pos);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 替换文本而不是删除
|
|
|
+ */
|
|
|
+ public static void replaceText(XWPFDocument document, String oldText, String newText) {
|
|
|
+ if (document == null || oldText == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 处理段落
|
|
|
+ for (XWPFParagraph paragraph : document.getParagraphs()) {
|
|
|
+ replaceTextInParagraph(paragraph, oldText, newText);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理表格
|
|
|
+ for (XWPFTable table : document.getTables()) {
|
|
|
+ for (XWPFTableRow row : table.getRows()) {
|
|
|
+ for (XWPFTableCell cell : row.getTableCells()) {
|
|
|
+ for (XWPFParagraph paragraph : cell.getParagraphs()) {
|
|
|
+ replaceTextInParagraph(paragraph, oldText, newText);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 替换段落中的文本
|
|
|
+ */
|
|
|
+ private static void replaceTextInParagraph(XWPFParagraph paragraph, String oldText, String newText) {
|
|
|
+ try {
|
|
|
+ String paragraphText = paragraph.getText();
|
|
|
+ if (paragraphText == null || !paragraphText.contains(oldText)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 替换文本
|
|
|
+ String updatedText = paragraphText.replace(oldText, newText != null ? newText : "");
|
|
|
+
|
|
|
+ // 重建段落
|
|
|
+ List<XWPFRun> runs = paragraph.getRuns();
|
|
|
+ for (int i = runs.size() - 1; i >= 0; i--) {
|
|
|
+ try {
|
|
|
+ paragraph.removeRun(i);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 忽略异常
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重新添加文本
|
|
|
+ if (!updatedText.trim().isEmpty()) {
|
|
|
+ XWPFRun run = paragraph.createRun();
|
|
|
+ run.setText(updatedText);
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 最简单直接的方法:删除包含特定文本的整个段落
|
|
|
+ */
|
|
|
+ public static void deleteParagraphsContainingText(XWPFDocument document, String textToFind) {
|
|
|
+ if (document == null || textToFind == null || textToFind.trim().isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 收集要删除的段落
|
|
|
+ List<XWPFParagraph> paragraphsToRemove = new ArrayList<>();
|
|
|
+
|
|
|
+ // 查找普通段落
|
|
|
+ for (XWPFParagraph paragraph : document.getParagraphs()) {
|
|
|
+ String text = paragraph.getText();
|
|
|
+ if (text != null && text.contains(textToFind)) {
|
|
|
+ paragraphsToRemove.add(paragraph);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从后往前删除,避免索引问题
|
|
|
+ for (int i = paragraphsToRemove.size() - 1; i >= 0; i--) {
|
|
|
+ XWPFParagraph paragraph = paragraphsToRemove.get(i);
|
|
|
+ try {
|
|
|
+ int pos = document.getPosOfParagraph(paragraph);
|
|
|
+ if (pos >= 0) {
|
|
|
+ document.removeBodyElement(pos);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 如果无法通过位置删除,尝试清空内容
|
|
|
+ List<XWPFRun> runs = paragraph.getRuns();
|
|
|
+ for (int j = runs.size() - 1; j >= 0; j--) {
|
|
|
+ try {
|
|
|
+ paragraph.removeRun(j);
|
|
|
+ } catch (Exception ex) {
|
|
|
+ // 忽略异常
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|