利用Java實(shí)現(xiàn)Word文檔自動(dòng)編號(hào)提取的方法詳解
一、背景
因業(yè)務(wù)需求,目前正在實(shí)現(xiàn)一項(xiàng)需求,即將一份試卷的內(nèi)容提取出來,由非結(jié)構(gòu)化到結(jié)構(gòu)化的轉(zhuǎn)換。在試卷解析的時(shí)候發(fā)現(xiàn)存在大綱目錄中帶有自動(dòng)編號(hào)的格式,按照傳統(tǒng)的方式?jīng)]有解析出來,花了一番功夫,最終還是搞定了,于是寫下來分享給大家。
二、概述
從一份word格式的試卷中,用java從XWPFParagraph中先判斷是否有編號(hào),如果有再提取自動(dòng)編號(hào),自動(dòng)編號(hào)還是會(huì)區(qū)分不同類型的,如中文的一、二、三等,阿拉伯?dāng)?shù)字的1、2、3等,或者是字母a、b、c等。
三、正文
1、需要先實(shí)現(xiàn)一個(gè)自動(dòng)編號(hào)上下文的class:
NumberingContext里面注冊(cè)了不同格式的編號(hào),如大寫字母編號(hào)、小寫字母編號(hào)、中文編號(hào)等,具體可以看代碼:
public class NumberingContext {
private final Map<String, List<Integer>> numberingStates = new HashMap<>();
// 編號(hào)格式映射(可以擴(kuò)展更多格式)
public static final Map<String, NumberFormatter> FORMATTERS = new HashMap<>();
static {
FORMATTERS.put("decimal", new DecimalFormatter());
FORMATTERS.put("lowerLetter", new LowerLetterFormatter());
FORMATTERS.put("upperLetter", new UpperLetterFormatter()); // 新增
FORMATTERS.put("upperRoman", new UpperRomanFormatter());
FORMATTERS.put("chineseCounting", new ChineseCountingFormatter());
}
public String resolveNumberText(String numIdKey, int ilvl, String format, String textTemplate) {
List<Integer> counters = numberingStates.computeIfAbsent(numIdKey, k -> new ArrayList<>());
// 確保層級(jí)計(jì)數(shù)器足夠長(zhǎng)
while (counters.size() <= ilvl) {
counters.add(0);
}
// 更新當(dāng)前層級(jí)計(jì)數(shù)器,并清空下級(jí)
counters.set(ilvl, counters.get(ilvl) + 1);
for (int i = ilvl + 1; i < counters.size(); i++) {
counters.set(i, 0);
}
// 獲取格式化器
NumberFormatter formatter = FORMATTERS.get(format);
if (formatter == null) {
formatter = new DecimalFormatter();
}
// 替換模板中的占位符
String result = textTemplate;
for (int i = 0; i <= ilvl; i++) {
String placeholder = "%" + (i + 1);
if (i < counters.size()) {
String replacement = formatter.format(counters.get(i));
result = result.replace(placeholder, replacement);
}
}
return result;
}
}2、不同類型的編號(hào)格式
1、DecimalFormatter
public class DecimalFormatter implements NumberFormatter {
@Override
public String format(int number) {
return String.valueOf(number);
}
}2、LowerLetterFormatter
public class LowerLetterFormatter implements NumberFormatter {
@Override
public String format(int number) {
return String.valueOf((char) ('a' + number - 1));
}
}3、UpperLetterFormatter
public class UpperLetterFormatter implements NumberFormatter {
@Override
public String format(int number) {
if (number < 1 || number > 26) {
throw new IllegalArgumentException("upperLetter 格式僅支持 1-26");
}
return String.valueOf((char) ('A' + number - 1));
}
}3、接下來就是核心邏輯:
這個(gè)getParagraphNumbering方法就是可以從XWPFParagraph
提取自動(dòng)的編號(hào)
public static String getParagraphNumbering(NumberingContext numberingContext, XWPFParagraph paragraph) {
try {
if (paragraph.getNumID() == null) {
//"無編號(hào)";
return "";
}
BigInteger numId = paragraph.getNumID();
BigInteger ilvl = paragraph.getNumIlvl() != null ? paragraph.getNumIlvl() : new BigInteger("0");
// 使用numId和文檔信息創(chuàng)建唯一鍵
String numIdKey = numId.toString();
XWPFNumbering numbering = paragraph.getDocument().getNumbering();
if (numbering == null) {
//"無編號(hào)定義";
return "";
}
XWPFNum num = numbering.getNum(numId);
if (num == null) {
//"編號(hào)定義不存在";
return "";
}
BigInteger abstractNumId = num.getCTNum().getAbstractNumId().getVal();
for (XWPFAbstractNum abstractNum : numbering.getAbstractNums()) {
if (abstractNum.getCTAbstractNum().getAbstractNumId().equals(abstractNumId)) {
CTAbstractNum ctAbstractNum = abstractNum.getCTAbstractNum();
CTLvl ctLvl = ctAbstractNum.getLvlArray(ilvl.intValue());
if (ctLvl != null && ctLvl.getNumFmt() != null && ctLvl.getLvlText() != null) {
String format = ctLvl.getNumFmt().getVal().toString();
String text = ctLvl.getLvlText().getVal();
return numberingContext.resolveNumberText(
numIdKey, ilvl.intValue(), format, text);
}
}
}
//"編號(hào)層級(jí)不存在或定義不完整";
return "";
} catch (Exception e) {
//"解析編號(hào)時(shí)出錯(cuò)";
return "";
}
}到此這篇關(guān)于利用Java實(shí)現(xiàn)Word文檔自動(dòng)編號(hào)提取的方法詳解的文章就介紹到這了,更多相關(guān)Java Word文檔自動(dòng)編號(hào)提取內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot詳解自定義Stater的應(yīng)用
Springboot的出現(xiàn)極大的簡(jiǎn)化了開發(fā)人員的配置,而這之中的一大利器便是springboot的starter,starter是springboot的核心組成部分,springboot官方同時(shí)也為開發(fā)人員封裝了各種各樣方便好用的starter模塊2022-07-07
Flink實(shí)現(xiàn)特定統(tǒng)計(jì)的歸約聚合reduce操作
這篇文章主要介紹了Flink實(shí)現(xiàn)特定統(tǒng)計(jì)的歸約聚合reduce操作,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-02-02
SpringBoot 過濾器, 攔截器, 監(jiān)聽器的具體使用
本文主要介紹了SpringBoot 過濾器, 攔截器, 監(jiān)聽器的具體使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
使用Okhttp實(shí)現(xiàn)上傳文件+參數(shù)請(qǐng)求接口form-data
在進(jìn)行接口對(duì)接時(shí),常遇到需要傳遞多種類型參數(shù)及文件上傳的情況,解決此問題的關(guān)鍵在于參數(shù)傳遞和文件上傳的正確處理,在Service層和Controller層的傳參,可以通過@RequestParam標(biāo)注或直接使用請(qǐng)求實(shí)體類,但若結(jié)合文件上傳,則不應(yīng)使用@RequestBody注解2024-10-10
Maven的國(guó)內(nèi)鏡像(快速解決jar下載過慢的問題)
下面小編就為大家?guī)硪黄狹aven的國(guó)內(nèi)鏡像(快速解決jar下載過慢的問題)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06

