使用 Apache poi 从 word 文档中提取部分的最佳方法是什么?
How is the best way to extract a section from word document with Apache poi?
我在 java 11 中将 Apache poi (XWPF) 与 Springboot 一起使用。
我需要使用以下数字列表从 word 文档中提取第 2 节(标题和内容):
word_example
我想知道只获取第 2 节及其内容以创建仅包含该部分的新 word 文档的最佳方法是什么。
private void extractAllParagraphs(){
//Get documet
XWPFDocument doc = new XWPFDocument(OPCPackage.open("path..."));
String textPart = "";
// loop all paragraphs
List<XWPFParagraph> xwpfParagraphList = doc.getParagraphs();
for (XWPFParagraph p : doc.getParagraphs()) {
//Get paragraph runs
List<XWPFRun> runs = p.getRuns();
//Loop runs of the paragraph
for(int i = 0; i<runs.size(); i++) {
textPart += runs.get(i).toString();
}
System.out.println(textPart);
}
}
谢谢。
XWPF
of apache poi
不能很好地支持 Word 中的编号。所以处理编号并不是很简单。
在 Word 中编号的段落有 num-id 和文档中设置的编号级别。此 num-id 引用单独编号文档部分中的编号。那里定义了编号类型(十进制、字母、罗马...)和编号格式。段落的实际编号由num-id、编号级别、编号类型、编号格式和文档中具有相同num-id的段落数决定.所以在阅读Word文档时管理编号真的很复杂。
以下工作草案显示了 一个 示例,说明如何在使用 apache poi
阅读 Word 文档时管理编号。这是一个工作草案,以尽可能少的代码展示原理。它使用内存结构来存储文档中的编号级别计数器和前一个编号级别。代码被附加注释以显示它的作用。
import java.io.FileInputStream;
import org.apache.poi.xwpf.usermodel.*;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.math.BigInteger;
public class WordReader {
//memory structure for storing the numbering level counter
private Map<Integer, Map<Integer, Integer>> numIDLvlCnt = new HashMap<Integer, Map<Integer, Integer>>();
//memory structure for storing the previous numbering level
private Map<Integer, Integer> numIDPrevNumIlv = new HashMap<Integer,Integer>();
private StringBuilder content = new StringBuilder();
private void traverseBodyElements(List<IBodyElement> bodyElements, boolean crlf) throws Exception {
for (IBodyElement bodyElement : bodyElements) {
if (bodyElement instanceof XWPFParagraph) {
XWPFParagraph paragraph = (XWPFParagraph)bodyElement;
//System.out.println(paragraph);
//ToDo: Do something with paragraph.
String no = "";
if (paragraph.getNumID() != null) { //if paragraph has numbering
no = getCurrentNumber(paragraph);
}
//print paragraph, if numbered then with leading number
content.append("<p>");
if (no.length() > 0) content.append(no + " ");
content.append(paragraph.getText());
content.append("</p>");
if (crlf) content.append("\r\n");
} else if (bodyElement instanceof XWPFTable) {
XWPFTable table = (XWPFTable)bodyElement;
//System.out.println(table);
content.append("<table>");
content.append("\r\n");
traverseTableRows(table.getRows());
content.append("</table>");
content.append("\r\n");
} // ToDo: else ...
}
}
private void traverseTableRows(List<XWPFTableRow> tableRows) throws Exception {
for (XWPFTableRow tableRow : tableRows) {
//System.out.println(tableRow);
content.append("<tr>");
traverseTableCells(tableRow.getTableICells());
content.append("</tr>");
content.append("\r\n");
}
}
private void traverseTableCells(List<ICell> tableICells) throws Exception {
for (ICell tableICell : tableICells) {
if (tableICell instanceof XWPFTableCell) {
XWPFTableCell tableCell = (XWPFTableCell)tableICell;
//System.out.println(tableCell);
content.append("<td>");
traverseBodyElements(tableCell.getBodyElements(), false);
content.append("</td>");
} // ToDo: else ...
}
}
//set numbering level counter for current numbering ID and numbering level
private void setNumIDLvlCnt(Integer numID, Integer numIlvl) {
if (numID != null) {
//get level counter for numbering ID
Map<Integer, Integer> lvlCnt = numIDLvlCnt.get(numID);
if (lvlCnt == null) { //if there is no level counter, create a new one
lvlCnt = new HashMap<Integer, Integer>();
numIDLvlCnt.put(numID, lvlCnt);
}
Integer prevNumIlv = numIDPrevNumIlv.get(numID);
if (prevNumIlv == null) {
prevNumIlv = 0;
numIDPrevNumIlv.put(numID, prevNumIlv);
}
if (numIlvl != null) {
//if this level is lower than the previous one, then all deeper level counters needs starting new
if (numIlvl < prevNumIlv) {
/*
for(Iterator<Integer> iterator = lvlCnt.keySet().iterator(); iterator.hasNext(); ) {
Integer ilvl = iterator.next();
if (ilvl > numIlvl) {
iterator.remove();
}
}
*/
lvlCnt.keySet().removeIf(ilvl -> ilvl > numIlvl);
}
//get current counter for level
Integer cnt = lvlCnt.get(numIlvl);
if (cnt == null) { //if there is no counter, set 0
lvlCnt.put(numIlvl, 0);
}
cnt = lvlCnt.get(numIlvl);
lvlCnt.put(numIlvl, cnt + 1); //count up 1
prevNumIlv = numIlvl; //set this level to be the previous level
numIDPrevNumIlv.put(numID, prevNumIlv);
}
}
//System.out.println(numIDLvlCnt);
//System.out.println(numIDPrevNumIlv);
}
//get formatted number from number format and level counter
private String getNoFromCount(String numFmt, Integer cnt) {
String no = "";
if ("DECIMAL".equalsIgnoreCase(numFmt)) {
no = String.valueOf(cnt);
} else if ("LOWERLETTER".equalsIgnoreCase(numFmt)) {
no = Character.toString(96 + cnt); //should be done better
} else if ("LOWERROMAN".equalsIgnoreCase(numFmt)) {
String[] romans = new String[]{"", "i", "ii", "iii", "iv", "v"};
if (cnt < romans.length) no = romans[cnt]; //should be done better
} else if ("UPPERROMAN".equalsIgnoreCase(numFmt)) {
String[] romans = new String[]{"", "I", "II", "III", "IV", "V"};
if (cnt < romans.length) no = romans[cnt]; //should be done better
} //ToDo: else ...
return no;
}
//get current number from paragraph
private String getCurrentNumber(XWPFParagraph paragraph) {
String no = "";
BigInteger numStartOverride = paragraph.getNumStartOverride(); //ToDo: to take into account
//System.out.println(numStartOverride);
//get numbering format
String numFmt = paragraph.getNumFmt(); //decimal, lowerletter, roman, ..
//get numbering ID
BigInteger numID = paragraph.getNumID();
//get current numbering level
BigInteger numIlvl = paragraph.getNumIlvl();
//set numbering level counter for current numbering ID and numbering level
setNumIDLvlCnt(numID.intValue(), numIlvl.intValue());
//get level counter for this numbering ID
Map<Integer, Integer> lvlCnt = numIDLvlCnt.get(numID.intValue());
//get numbering level text
String numLevelText = paragraph.getNumLevelText(); // %1.%2.%3...
no = numLevelText;
for (Integer ilvl : lvlCnt.keySet()) {
int i = ilvl + 1;
//replace the placeholders %1, %2, %3, ... with formatted number from number format and level counter
no = no.replace("%"+i, getNoFromCount(numFmt, lvlCnt.get(ilvl)));
}
return no;
}
public void read(String inFilePath) throws Exception {
XWPFDocument document = new XWPFDocument(new FileInputStream(inFilePath));
traverseBodyElements(document.getBodyElements(), true);
document.close();
System.out.println(content);
}
public static void main(String[] args) throws Exception {
String inFilePath = "./WordDocument.docx";
WordReader reader = new WordReader();
reader.read(inFilePath);
}
}
注意:由于您的问题询问的是最佳方式:这显示了一个方式。它是否是“最好的”在这里不予回答。关于“最好的方法”的问题总是基于意见,所以不是要在这里问的问题。参见 https://whosebug.com/help/on-topic。
我在 java 11 中将 Apache poi (XWPF) 与 Springboot 一起使用。
我需要使用以下数字列表从 word 文档中提取第 2 节(标题和内容):
word_example
我想知道只获取第 2 节及其内容以创建仅包含该部分的新 word 文档的最佳方法是什么。
private void extractAllParagraphs(){
//Get documet
XWPFDocument doc = new XWPFDocument(OPCPackage.open("path..."));
String textPart = "";
// loop all paragraphs
List<XWPFParagraph> xwpfParagraphList = doc.getParagraphs();
for (XWPFParagraph p : doc.getParagraphs()) {
//Get paragraph runs
List<XWPFRun> runs = p.getRuns();
//Loop runs of the paragraph
for(int i = 0; i<runs.size(); i++) {
textPart += runs.get(i).toString();
}
System.out.println(textPart);
}
}
谢谢。
XWPF
of apache poi
不能很好地支持 Word 中的编号。所以处理编号并不是很简单。
在 Word 中编号的段落有 num-id 和文档中设置的编号级别。此 num-id 引用单独编号文档部分中的编号。那里定义了编号类型(十进制、字母、罗马...)和编号格式。段落的实际编号由num-id、编号级别、编号类型、编号格式和文档中具有相同num-id的段落数决定.所以在阅读Word文档时管理编号真的很复杂。
以下工作草案显示了 一个 示例,说明如何在使用 apache poi
阅读 Word 文档时管理编号。这是一个工作草案,以尽可能少的代码展示原理。它使用内存结构来存储文档中的编号级别计数器和前一个编号级别。代码被附加注释以显示它的作用。
import java.io.FileInputStream;
import org.apache.poi.xwpf.usermodel.*;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.math.BigInteger;
public class WordReader {
//memory structure for storing the numbering level counter
private Map<Integer, Map<Integer, Integer>> numIDLvlCnt = new HashMap<Integer, Map<Integer, Integer>>();
//memory structure for storing the previous numbering level
private Map<Integer, Integer> numIDPrevNumIlv = new HashMap<Integer,Integer>();
private StringBuilder content = new StringBuilder();
private void traverseBodyElements(List<IBodyElement> bodyElements, boolean crlf) throws Exception {
for (IBodyElement bodyElement : bodyElements) {
if (bodyElement instanceof XWPFParagraph) {
XWPFParagraph paragraph = (XWPFParagraph)bodyElement;
//System.out.println(paragraph);
//ToDo: Do something with paragraph.
String no = "";
if (paragraph.getNumID() != null) { //if paragraph has numbering
no = getCurrentNumber(paragraph);
}
//print paragraph, if numbered then with leading number
content.append("<p>");
if (no.length() > 0) content.append(no + " ");
content.append(paragraph.getText());
content.append("</p>");
if (crlf) content.append("\r\n");
} else if (bodyElement instanceof XWPFTable) {
XWPFTable table = (XWPFTable)bodyElement;
//System.out.println(table);
content.append("<table>");
content.append("\r\n");
traverseTableRows(table.getRows());
content.append("</table>");
content.append("\r\n");
} // ToDo: else ...
}
}
private void traverseTableRows(List<XWPFTableRow> tableRows) throws Exception {
for (XWPFTableRow tableRow : tableRows) {
//System.out.println(tableRow);
content.append("<tr>");
traverseTableCells(tableRow.getTableICells());
content.append("</tr>");
content.append("\r\n");
}
}
private void traverseTableCells(List<ICell> tableICells) throws Exception {
for (ICell tableICell : tableICells) {
if (tableICell instanceof XWPFTableCell) {
XWPFTableCell tableCell = (XWPFTableCell)tableICell;
//System.out.println(tableCell);
content.append("<td>");
traverseBodyElements(tableCell.getBodyElements(), false);
content.append("</td>");
} // ToDo: else ...
}
}
//set numbering level counter for current numbering ID and numbering level
private void setNumIDLvlCnt(Integer numID, Integer numIlvl) {
if (numID != null) {
//get level counter for numbering ID
Map<Integer, Integer> lvlCnt = numIDLvlCnt.get(numID);
if (lvlCnt == null) { //if there is no level counter, create a new one
lvlCnt = new HashMap<Integer, Integer>();
numIDLvlCnt.put(numID, lvlCnt);
}
Integer prevNumIlv = numIDPrevNumIlv.get(numID);
if (prevNumIlv == null) {
prevNumIlv = 0;
numIDPrevNumIlv.put(numID, prevNumIlv);
}
if (numIlvl != null) {
//if this level is lower than the previous one, then all deeper level counters needs starting new
if (numIlvl < prevNumIlv) {
/*
for(Iterator<Integer> iterator = lvlCnt.keySet().iterator(); iterator.hasNext(); ) {
Integer ilvl = iterator.next();
if (ilvl > numIlvl) {
iterator.remove();
}
}
*/
lvlCnt.keySet().removeIf(ilvl -> ilvl > numIlvl);
}
//get current counter for level
Integer cnt = lvlCnt.get(numIlvl);
if (cnt == null) { //if there is no counter, set 0
lvlCnt.put(numIlvl, 0);
}
cnt = lvlCnt.get(numIlvl);
lvlCnt.put(numIlvl, cnt + 1); //count up 1
prevNumIlv = numIlvl; //set this level to be the previous level
numIDPrevNumIlv.put(numID, prevNumIlv);
}
}
//System.out.println(numIDLvlCnt);
//System.out.println(numIDPrevNumIlv);
}
//get formatted number from number format and level counter
private String getNoFromCount(String numFmt, Integer cnt) {
String no = "";
if ("DECIMAL".equalsIgnoreCase(numFmt)) {
no = String.valueOf(cnt);
} else if ("LOWERLETTER".equalsIgnoreCase(numFmt)) {
no = Character.toString(96 + cnt); //should be done better
} else if ("LOWERROMAN".equalsIgnoreCase(numFmt)) {
String[] romans = new String[]{"", "i", "ii", "iii", "iv", "v"};
if (cnt < romans.length) no = romans[cnt]; //should be done better
} else if ("UPPERROMAN".equalsIgnoreCase(numFmt)) {
String[] romans = new String[]{"", "I", "II", "III", "IV", "V"};
if (cnt < romans.length) no = romans[cnt]; //should be done better
} //ToDo: else ...
return no;
}
//get current number from paragraph
private String getCurrentNumber(XWPFParagraph paragraph) {
String no = "";
BigInteger numStartOverride = paragraph.getNumStartOverride(); //ToDo: to take into account
//System.out.println(numStartOverride);
//get numbering format
String numFmt = paragraph.getNumFmt(); //decimal, lowerletter, roman, ..
//get numbering ID
BigInteger numID = paragraph.getNumID();
//get current numbering level
BigInteger numIlvl = paragraph.getNumIlvl();
//set numbering level counter for current numbering ID and numbering level
setNumIDLvlCnt(numID.intValue(), numIlvl.intValue());
//get level counter for this numbering ID
Map<Integer, Integer> lvlCnt = numIDLvlCnt.get(numID.intValue());
//get numbering level text
String numLevelText = paragraph.getNumLevelText(); // %1.%2.%3...
no = numLevelText;
for (Integer ilvl : lvlCnt.keySet()) {
int i = ilvl + 1;
//replace the placeholders %1, %2, %3, ... with formatted number from number format and level counter
no = no.replace("%"+i, getNoFromCount(numFmt, lvlCnt.get(ilvl)));
}
return no;
}
public void read(String inFilePath) throws Exception {
XWPFDocument document = new XWPFDocument(new FileInputStream(inFilePath));
traverseBodyElements(document.getBodyElements(), true);
document.close();
System.out.println(content);
}
public static void main(String[] args) throws Exception {
String inFilePath = "./WordDocument.docx";
WordReader reader = new WordReader();
reader.read(inFilePath);
}
}
注意:由于您的问题询问的是最佳方式:这显示了一个方式。它是否是“最好的”在这里不予回答。关于“最好的方法”的问题总是基于意见,所以不是要在这里问的问题。参见 https://whosebug.com/help/on-topic。