首页 > 编程语言 >【Java】Word题库解析

【Java】Word题库解析

时间:2024-08-16 09:20:04浏览次数:5  
标签:Java String text content DbcpExamUtil RoughItem 题库 Word public

一、需求场景:

一共四种题型,单选、多选、判断、简答

题目构成要素:题目、选项、答案、解析

一种题型一个Word文档存放,需要把这些题目写入DB维护

 

二、题库格式:

单选案例:

多选案例:

 判断案例:

简答题案例:

可以看出,单选,多选和判断都是一样的

- 题目有数字和点开头,并设置了标题样式

- 选项由ABCDEF和点组成

- 每一个答案的前缀固定有【答案:】

- 每一个解析的前缀固定有【解析:】

简答题的部分组成没有选项,只有题目 + 答案

三、解析实现

依赖poi实现,mvn坐标:

<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>

文档读取:

@SneakyThrows
public static XWPFDocument getWordFile(String path) {
    FileInputStream fileInputStream = new FileInputStream(path);
    XWPFDocument xwpfDocument = new XWPFDocument(fileInputStream);
    fileInputStream.close();
    return xwpfDocument;
}

获取所有段落:

List<XWPFParagraph> paragraphs = xwpfDocument.getParagraphs();  

根据格式得知,每一个题目和题型都是一个段落,选项,答案,解析也是段落

相互之间没有关联性,和上一次的HTML报告相似

但是每个标题存在一个序号数前缀,使用一个迭代值进行计数

循环至下一个带序号数前缀的段落对象时,就是下一道题目了

 

为了保存每次读取的段落,需要创建一个原始的Item类

序列值用来分组管理,把题目、选项、答案、解析合并起来

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ToString
public static final class RoughItem {
    public int serial;
    public String content;
}

最终要保存成一个题目对象

题目对象只有四个属性,题目、题型、答案、解析

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ToString
public static final class ExamItem {
    public String title;
    public String type;
    public String answer;
    public String explain;
}

  

完整工具类实现:

package jnpf.util;

import lombok.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;

import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

public class DbcpExamUtil {

    private static final List<String> OPTIONS = Arrays.asList("A", "B", "C", "D", "E", "F", "G");;
    private static final String ANSWER_PREFIX = "答案:";
    private static final  String EXPLAIN_PREFIX = "解析:";
    private static final String NUMBER_REGEXP = "^[1-9]\\d*";

    private static final String TYPE1_RADIO = "0";
    private static final String TYPE2_CHECKBOX = "1";
    private static final String TYPE3_TRUE_OR_FASE = "2";
    private static final String TYPE4_SHORT_QA = "3";
    private static final String SPLIT_IDENTIFY = "\\.";


    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    @ToString
    public static final class RoughItem {
        public int serial;
        public String content;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    @ToString
    public static final class ExamItem {
        public String title;
        public String type;
        public String answer;
        public String explain;
    }

    @SneakyThrows
    public static XWPFDocument getWordFile(String path) {
        FileInputStream fileInputStream = new FileInputStream(path);
        XWPFDocument xwpfDocument = new XWPFDocument(fileInputStream);
        fileInputStream.close();
        return xwpfDocument;
    }

    @SneakyThrows
    public static void radioTypeRead(String path, Consumer<ExamItem> consumer) {
        XWPFDocument xwpfDocument = getWordFile(path);
        int examCount = 0;
        List<DbcpExamUtil.RoughItem> roughItems = new ArrayList<>();
        List<XWPFParagraph> paragraphs = xwpfDocument.getParagraphs();
        for (XWPFParagraph xwpfParagraph : paragraphs) {
            String text = xwpfParagraph.getText();
            /* 无内容段落跳过 */
            if (StringUtils.isBlank(text)) continue;
            /* 按点号分割字符串 */
            String[] split = text.split(SPLIT_IDENTIFY);
            /* 首个字符串是否匹配数值序号 */
            boolean isExamNo = split[0].matches(NUMBER_REGEXP);
            /* 是否为选项 */
            boolean isOptions = OPTIONS.contains(split[0]);
            /* 是否为答案 */
            boolean isAnswer = text.startsWith(ANSWER_PREFIX);
            /* 是否为解析 */
            boolean isExplain = text.startsWith(EXPLAIN_PREFIX);
            /* 当判断为题目序列时,迭代计数变量,是一道新的题目 */
            if (isExamNo) {
                ++ examCount;
                DbcpExamUtil.RoughItem roughItem = DbcpExamUtil.RoughItem.builder().serial(examCount).content(text).build() ;
                roughItems.add(roughItem);
            } else if (isOptions || isAnswer || isExplain) {
                /* 反之不是题目序列,而是选项,答案,解析时,保存起来 */
                DbcpExamUtil.RoughItem roughItem = DbcpExamUtil.RoughItem.builder().serial(examCount).content(text).build() ;
                roughItems.add(roughItem);
            }
        }
        /* 收集完成后使用序列进行分组处理 */
        Map<Integer, List<RoughItem>> listMap = roughItems.stream().collect(Collectors.groupingBy(DbcpExamUtil.RoughItem::getSerial));
        listMap.forEach((k, v) -> {
            /* 第一项一定是题目 */
            RoughItem titleItem = v.get(0);
            String content = titleItem.getContent();
            /* 将选项和题目合并为题目 */
            String collect = v.parallelStream().map(RoughItem::getContent).filter(xContent -> OPTIONS.contains(xContent.split("\\.")[0])).collect(Collectors.joining("\n"));
            content = content + "\n" + collect;
            /* 处理集合得到答案和解析,解析不一定存在,所以orElse设置空串默认值 */
            String answer = v.parallelStream().map(RoughItem::getContent).filter(xContent -> xContent.startsWith(ANSWER_PREFIX)).map(x -> x.replace(ANSWER_PREFIX, "")).findFirst().orElse("");
            String explain = v.parallelStream().map(RoughItem::getContent).filter(xContent -> xContent.startsWith(EXPLAIN_PREFIX)).map(x -> x.replace(EXPLAIN_PREFIX, "")).findFirst().orElse("");
            /* 包装成题目对象后给调用者消费 */
            consumer.accept(ExamItem
                    .builder()
                    .title(content)
                    .type(TYPE1_RADIO)
                    .answer(answer)
                    .explain(explain)
                    .build());
        });

    }

    @SneakyThrows
    public static void checkBoxTypeRead(String path, Consumer<ExamItem> consumer) {
        int examCount = 0;
        List<DbcpExamUtil.RoughItem> roughItems = new ArrayList<>();
        XWPFDocument xwpfDocument = getWordFile(path);
        List<XWPFParagraph> paragraphs = xwpfDocument.getParagraphs();
        for (XWPFParagraph xwpfParagraph : paragraphs) {
            String text = xwpfParagraph.getText();
            if (StringUtils.isBlank(text)) continue;
            String[] split = text.split(SPLIT_IDENTIFY);
            boolean isExamNo = split[0].matches(NUMBER_REGEXP);
            boolean isOptions = OPTIONS.contains(split[0]);
            boolean isAnswer = text.startsWith(ANSWER_PREFIX);
            boolean isExplain = text.startsWith(EXPLAIN_PREFIX);
            if (isExamNo) {
                ++ examCount;
                DbcpExamUtil.RoughItem roughItem = DbcpExamUtil.RoughItem.builder().serial(examCount).content(text).build() ;
                roughItems.add(roughItem);
            } else if (isOptions || isAnswer || isExplain) {
                DbcpExamUtil.RoughItem roughItem = DbcpExamUtil.RoughItem.builder().serial(examCount).content(text).build() ;
                roughItems.add(roughItem);
            }
        }
        System.out.println(examCount);
        Map<Integer, List<DbcpExamUtil.RoughItem>> listMap = roughItems.stream().collect(Collectors.groupingBy(DbcpExamUtil.RoughItem::getSerial));
        listMap.forEach((k, v) -> {
            RoughItem titleItem = v.get(0);
            String content = titleItem.getContent();
            String collect = v.parallelStream().map(RoughItem::getContent).filter(xContent -> OPTIONS.contains(xContent.split("\\.")[0])).collect(Collectors.joining("\n"));
            content = content + "\n" + collect;
            String answer = v.parallelStream().map(RoughItem::getContent).filter(xContent -> xContent.startsWith(ANSWER_PREFIX)).map(x -> x.replace(ANSWER_PREFIX, "")).findFirst().orElse("");
            String explain = v.parallelStream().map(RoughItem::getContent).filter(xContent -> xContent.startsWith(EXPLAIN_PREFIX)).map(x -> x.replace(EXPLAIN_PREFIX, "")).findFirst().orElse("");
            consumer.accept(ExamItem
                    .builder()
                    .title(content)
                    .type(TYPE2_CHECKBOX)
                    .answer(answer)
                    .explain(explain)
                    .build());
        });
    }

    @SneakyThrows
    public static void trueOrFalseTypeRead(String path, Consumer<ExamItem> consumer) {
        int examCount = 0;
        List<DbcpExamUtil.RoughItem> roughItems = new ArrayList<>();
        XWPFDocument xwpfDocument = getWordFile(path);
        List<XWPFParagraph> paragraphs = xwpfDocument.getParagraphs();
        for (XWPFParagraph xwpfParagraph : paragraphs) {
            String text = xwpfParagraph.getText();
            if (StringUtils.isBlank(text)) continue;
            String[] split = text.split(SPLIT_IDENTIFY);
            boolean isExamNo = split[0].matches(NUMBER_REGEXP);
            boolean isOptions = OPTIONS.contains(split[0]);
            boolean isAnswer = text.startsWith(ANSWER_PREFIX);
            boolean isExplain = text.startsWith(EXPLAIN_PREFIX);
            if (isExamNo) {
                ++ examCount;
                DbcpExamUtil.RoughItem roughItem = DbcpExamUtil.RoughItem.builder().serial(examCount).content(text).build() ;
                roughItems.add(roughItem);
            } else if (isOptions || isAnswer || isExplain) {
                DbcpExamUtil.RoughItem roughItem = DbcpExamUtil.RoughItem.builder().serial(examCount).content(text).build() ;
                roughItems.add(roughItem);
            }
        }
        System.out.println(examCount);
        Map<Integer, List<DbcpExamUtil.RoughItem>> listMap = roughItems.stream().collect(Collectors.groupingBy(DbcpExamUtil.RoughItem::getSerial));
        listMap.forEach((k, v) -> {
            RoughItem titleItem = v.get(0);
            String content = titleItem.getContent();
            String collect = v.parallelStream().map(RoughItem::getContent).filter(xContent -> OPTIONS.contains(xContent.split("\\.")[0])).collect(Collectors.joining("\n"));
            content = content + "\n" + collect;
            String answer = v.parallelStream().map(RoughItem::getContent).filter(xContent -> xContent.startsWith(ANSWER_PREFIX)).map(x -> x.replace(ANSWER_PREFIX, "")).findFirst().orElse("");
            String explain = v.parallelStream().map(RoughItem::getContent).filter(xContent -> xContent.startsWith(EXPLAIN_PREFIX)).map(x -> x.replace(EXPLAIN_PREFIX, "")).findFirst().orElse("");
            consumer.accept(ExamItem
                    .builder()
                    .title(content)
                    .type(TYPE3_TRUE_OR_FASE)
                    .answer(answer)
                    .explain(explain)
                    .build());
        });
    }

    public static void shortQaTypeRead(String path, Consumer<ExamItem> consumer) {
        int examCount = 0;
        List<DbcpExamUtil.RoughItem> roughItems = new ArrayList<>();
        XWPFDocument xwpfDocument = getWordFile(path);
        List<XWPFParagraph> paragraphs = xwpfDocument.getParagraphs();
        for (XWPFParagraph xwpfParagraph : paragraphs) {
            String text = xwpfParagraph.getText();
            if (StringUtils.isBlank(text)) continue;
            String style = xwpfParagraph.getStyle();
            boolean isTittle = StringUtils.isNotBlank(style);
            if (isTittle) {
                ++ examCount;
                DbcpExamUtil.RoughItem roughItem = DbcpExamUtil.RoughItem.builder().serial(examCount).content(text).build() ;
                roughItems.add(roughItem);
            } else {
                DbcpExamUtil.RoughItem roughItem = DbcpExamUtil.RoughItem.builder().serial(examCount).content(text).build() ;
                roughItems.add(roughItem);
            }
        }
        Map<Integer, List<DbcpExamUtil.RoughItem>> listMap = roughItems.stream().collect(Collectors.groupingBy(DbcpExamUtil.RoughItem::getSerial));
        listMap.forEach((k, v) -> {
            RoughItem titleItem = v.get(0);
            String content = titleItem.getContent();
            String answer = v.stream().skip(1).map(RoughItem::getContent).collect(Collectors.joining("\n"));
            consumer.accept(ExamItem
                    .builder()
                    .title(content)
                    .type(TYPE4_SHORT_QA)
                    .answer(answer)
                    .explain("")
                    .build());
        });
    }
}

  

调用工具方法:

@Override
public void qaImport() {
    String T1 = "D:\\exam-repo\\单选题-答案.docx";
    String T2 = "D:\\exam-repo\\多选题-答案.docx";
    String T3 = "D:\\exam-repo\\判断题-答案.docx";
    String T4 = "D:\\exam-repo\\简答题.docx";
    DbcpExamUtil.radioTypeRead(T1, ei -> {
        baseMapper.insert(TrnExQabank.builder()
                .qaSubject(ei.getTitle())
                .qaType(ei.getType())
                .qaAnswer(ei.getAnswer())
                .qaAnaly(ei.getExplain())
                .build());
    });
    DbcpExamUtil.checkBoxTypeRead(T2, ei -> {
        baseMapper.insert(TrnExQabank.builder()
                .qaSubject(ei.getTitle())
                .qaType(ei.getType())
                .qaAnswer(ei.getAnswer())
                .qaAnaly(ei.getExplain())
                .build());
    });
    DbcpExamUtil.trueOrFalseTypeRead(T3, ei -> {
        baseMapper.insert(TrnExQabank.builder()
                .qaSubject(ei.getTitle())
                .qaType(ei.getType())
                .qaAnswer(ei.getAnswer())
                .qaAnaly(ei.getExplain())
                .build());
    });
    DbcpExamUtil.shortQaTypeRead(T4, ei -> {
        baseMapper.insert(TrnExQabank.builder()
                .qaSubject(ei.getTitle())
                .qaType(ei.getType())
                .qaAnswer(ei.getAnswer())
                .qaAnaly(ei.getExplain())
                .build());
    });
}

  

 

标签:Java,String,text,content,DbcpExamUtil,RoughItem,题库,Word,public
From: https://www.cnblogs.com/mindzone/p/18362194

相关文章

  • 0-JavaScript入门
    1.1JS用途JavaScript属于一门脚本语言,可用于前端给网页添加一些动态效果。也可用于node平台,开发服务器React、Vue框架底层也是通过JS实现1.2JS组成前端分为三层层次用途HTML(结构层)利用语义化标签搭建网页CSS(样式层)利用样式进行美化网页、进行网页布局J......
  • 用JavaScript做超级玛丽小游戏
    一、前言前几天用JS实现扫雷和贪吃蛇(通过HTML的DOM节点实现基本界面,界面背景简单,交互简单)。比较复杂的是植物大战僵尸,不同的关卡设置单独的函数。所以还比较难。超级玛丽通过canvas实现背景,交互很复杂,功能很多,JS代码完全是有汇编语言反编译成C语言,然后把C语言转换成JS实现的......
  • 计算机毕业设计必看必学!! 93494基于springboot 书店信息管理系统,原创定制程序, java
    摘 要书店信息管理系统采用B/S结构、java开发语言、以及Mysql数据库等技术。系统主要分为管理员和用户两部分,管理员管理主要功能包括:首页、轮播图、公告栏、资源管理(图书资讯、资讯分类)交流管理(留言板、留言板分类)系统用户(管理员、顾客用户)模块管理(图书信息、分类管理、购......
  • 华为OD笔试机试 - 攀登者2 (python/c++/java 2024年C卷D卷真题算法)
    华为OD机试(C卷+D卷)2024真题目录(Java&c++&python)题目描述攀登者喜欢寻找各种地图,并且尝试攀登到最高的山峰。地图表示为一维数组,数组的索引代表水平位置,数组的元素代表相对海拔高度。其中数组元素0代表地面。例如:[0,1,2,4,3,1,0,0,1,2,3,1,2,1,0],代表如下图所示的......
  • Java异常 小白版
    什么是异常在程序运行时打断正常程序流程的任何不正常的情况称为错误或异常。异常包括用户造成的异常和系统造成的异常。例如:网络连接中断、操作符越界、加载的类找不到异常产生的原因和分类1.异常产生的原因在Java中异常产生,主要是有三种原因:编写程序代码中的错误产生......
  • Java中的四层框架
    一、实体层别名:model层,domain层,entity层用途:用于存放实体类,与数据库中的属性值基本保持一致,实现set和get的方法。二、mapper层别名:dao层用途:对数据库进行数据持久化操作,他的方法语句是直接针对数据库操作的,主要实现一些增删改查操作,在mybatis中方法主要与xxx.xml内相互一......
  • [Java并发]Synchronized底层原理
    synchronized底层语义原理Java虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现。在Java语言中,同步用的最多的地方可能是被synchronized修饰的同步方法。同步方法并不是由monitorenter和monitorexit指令来实现同步的,而是由方法调用指令读取运行时......
  • TypeScript 之 JavaScript文件类型检查
    启用对JavaScript文件的类型检查在TypeScript编译选项compilerOptions全部配置项中,可以通过以下2个属性配置JavaScriptSupport:allowJs是否允许编译JavaScript文件。默认值是false。在默认情况下,TypeScript编译器只处理.ts、.tsx和.d.ts文件,不会编译.js......
  • Java 入门指南:Bean 特殊的Java类
    JavaBeanJavaBean是一种符合特定约定的Java类,用于在Java程序中封装数据和行为。它是一种重要的编程模式,用于简化和统一对象的创建、访问和操作,使得其他Java类可以通过自省(反射)机制来发现和操作这些JavaBean的属性。JavaBean可以用于实现数据封装、数据传输、持久......
  • Java 入门指南:接口(Interface)
    引言在Java编程中,接口(Interface)是一种非常重要的概念,它不仅是面向对象编程(OOP)的基石之一,也是实现高内聚、低耦合设计原则的关键工具。接口定义了一组方法,但不提供这些方法的实现细节,而是由实现接口的类来具体实现。这种机制使得Java程序更加灵活、易于扩展和维护。定义接......