前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >批量添加题目功能(正则表达式的使用案例)

批量添加题目功能(正则表达式的使用案例)

作者头像
Daotin
发布2020-11-09 17:38:35
8860
发布2020-11-09 17:38:35
举报

问题描述

根据业务需求,需要有一个批量添加题目的功能。

如上图所示,左边是纯文本输入的题目,右边需要解析成一个个对应的题目。

右边的题目就是一个组件,根据不同的类型进行显示,这个组件这里不做谈论。这里要做的就是根据左边的文本内容,解析成一个个对象的形式,传入右边的组件进行渲染。

这篇文章,就是如何把左边的纯文本,解析成一个个的对象,这就考验文本的拆分能力了。

指定拆分规则

第一步当然是指定规则,不然没法知道以什么的方式进行解析。

这里为了方便处理,对每个题目类型的格式加以限定,每个题目之间也用空行进行分隔。

代码语言:javascript
复制
1、每个题目之间必须以空行分割,题干中间不得换行。
2、所有题型必须含有 “答案:”字段,且不能为空。
3、【选择题】最多支持10个选项A,B,C,D,E,F,G,H,I,J,且必须按顺序输入。
4、【选择题】选项号A-H与内容之间必须用 ”、“ 或 ”.“ 分隔开。
5、【选择题】答案中不能加空格。
6、【判断题】答案仅支持 “正确”,“错误” 或者 “对”,“错”。
7、【填空题】每个空使用至少三个短下划线 “___” 作为空的位置。
8、【填空题】多个填空的答案用 “|” 分割。每个填空有多个答案的话用 “&&” 分隔。单个答案不用添加。
9、【填空题】答案如果多于题目中需要填空的个数,多于的答案将被忽略。
10、【问答题】的答案可以为空。
11、【文件上传题】的答案必须为 “[文件]” 。

拆分题目

下一步就是拆分每个题目。

以为核心就是一句正则表达式 /\n\s*\n\s*/g,所以直接上代码:

代码语言:javascript
复制
// 将批量题目分成一个个题目字符串
function getEachSub(whole) {
    let me = this;
    me.previewSubjects = [];

    // 主要就是这句话
    let eachSourceSubs = whole.trim().split(/\n\s*\n\s*/g);
    if(eachSourceSubs.length) {
        eachSourceSubs.forEach(item=>{
            // assembleSub就是把每个题目的字符串转换成题目对象,保存到previewSubjects数组里
            me.previewSubjects.push(me.assembleSub(item.trim()));
        });
    }
}

题目字符串转为对象

题目的类型总共分为:

  • 单选题(radio)
  • 多选题(chk)
  • 判断题(judge)
  • 填空题(fillin)
  • 问答题(textarea)
  • 文件题(file)

每个题目的对象为:

代码语言:javascript
复制
{
    inpttype:'', // 类型
    subject:'', // 题干
    answer: '', // 正确答案
    items:[], // 题目选项(单选,多选,填空使用)
    err:'', // 题目解析有问题时的报错信息
}

解析的思路:

  • 由于每题都必须含有“答案”字段,所以通过答案进行拆分可以得到题干和正确答案内容。
    • 如果匹配到多个“答案”,则报错,因为每个题目只能有一个答案
  • 答案对了,判断题型
    • 如果题目中有A-Z开头的,那么就是单选题或者多选题
      • 如果是单选题或者多选题
        • 把从“答案”开始到末尾所有字符串取到,然后去掉“答案”本身就是这道题目的正确答案。 (如果正确答案长度>1,就是多选题,否则为单选题)
        • 剩下的部分为题干和选项。再通过选项前面的A-Z进行拆分后,数组的第一个就是题干,剩下的就是选项了。
        • 最后,判断答案与选项是否匹配。
      • 否则
        • 通过“答案”拆分后,数组的第一个就是题干
          • 如果题干中包含“___”就是填空题,它的答案选项就是在数组的第二个里面。
          • 如果数组的第二个是“对”“错”“正确”“错误”,它就是判断题,那么数组第一个就是题干了。
          • 如果数组的第二个是“[文件]”,它就是文件题了。
          • 剩下的就是问答题。

具体的代码如下:

代码语言:javascript
复制
// 将一个个题目字符串拆解/组合成题目对象
function assembleSub(eachSub) {
    let me = this;
    let subObj = {
        inpttype:'', // 类型
        subject:'', // 题干
        answer: '', // 正确答案
        items:[], // 题目选项
        err:'' // 错误提示
    };
    let ansArr = eachSub.match(/\n\s*答案[::]/g);

    if(ansArr) {
        if(ansArr.length>1) { // 匹配到多个答案
            subObj.err = '每道题只能有一个答案';
        } else {
            /**
             * 单选题和多选题
             */
            if (eachSub.search(/\n\s*[A-Z][\.、]/ig) > -1) {
                let selReg = /\n\s*答案[::]\s*[A-Z]+/i; // 单选题和多选题

                let selectAns = eachSub.match(selReg);

                if(selectAns) {
                    let ans = selectAns[0].trim().replace(/^答案[::]\s*/g, '').toUpperCase();
                    subObj.answer = ans;
                    // 单选题与多选题
                    subObj.inpttype = ans.length===1 ? 'radio' : 'chk';


                    let sourceTimu = eachSub.replace(selReg,'');
                    // 拆分题干与选项
                    let sourceTimuArr = sourceTimu.split(/[A-Z][、\.]/ig);
                    if(sourceTimuArr.length == 1){
                        subObj.err = '选项不能为空';
                    }else if(sourceTimuArr.length  > 11){
                        subObj.err = '选项数量不能大于10个';
                    }

                    let valArr = [];
                    sourceTimuArr.map((item,i)=>{
                        sourceTimuArr[i] = item.trim().replace(/\s+/g, ' ');

                        if(i===0) {
                            // 题干
                            subObj.subject = sourceTimuArr[i];
                        } else {
                            // 选项
                            let obj = {
                                pic:'',
                                text: sourceTimuArr[i],
                                val: String.fromCharCode(65+i-1) // ascii转字母
                            };
                            subObj.items.push(obj);
                            valArr.push(obj.val);
                        }
                    });

                    // 单选题
                    if(subObj.answer.length === 1) {
                        if(!valArr.includes(subObj.answer)) {
                            subObj.err= '答案选项不正确';
                        }
                    } else { // 多选题
                        for (let a of subObj.answer) {
                            if(!valArr.includes(a)) {
                                subObj.err= '答案选项不正确';
                                break;
                            }
                        }
                    }
                } else {
                    subObj.err = '选择题答案不正确';
                }
            }
            else {
                let reg = /\n\s*答案[::]\s*/g;
                let regArr = eachSub.split(reg);

                subObj.subject = regArr[0];
                /**
                 * 填空题
                 */
                if(regArr[0].includes('___')) {
                    subObj.inpttype = 'fillin';
                    let len = regArr[0].match(/_{3,}/g).length;
                    let fillinAns = regArr[1].split(/\s*\|\s*/g);

                    if(len>0 && len <= fillinAns.length) {
                        for(let i=0; i<len; i++) {
                            if(fillinAns[i].includes('&&')) {
                                subObj.items.push({
                                    matchtype: "equal",
                                    vals: fillinAns[i].split('&&').filter(item=>{return item})
                                });
                            } else {
                                subObj.items.push({
                                    matchtype: "equal",
                                    vals: [fillinAns[i]]
                                });
                            }
                        }
                    } else {
                        subObj.err = '填空题答案个数错误';
                    }
                }
                /**
                 * 判断题
                 */
                else if(regArr[1].trim() === '对'
                    || regArr[1].trim() === '错'
                    || regArr[1].trim() ==='正确'
                    || regArr[1].trim() ==='错误') {
                    subObj.inpttype = 'judge';

                    subObj.answer = (regArr[1].trim() === '对'|| regArr[1].trim() === '正确') ? '1' : '0';
                }
                /**
                 * 文件上传题
                 */
                else if(regArr[1].trim() === '[文件]'){
                    subObj.inpttype = 'file';
                }
                /**
                 * 问答题
                 */
                else {
                    subObj.inpttype = 'textarea';
                    subObj.answer = regArr[1].trim();
                }
            }
        }
    } else { // 未匹配到则为null
        subObj.err = '题目缺少答案'
    }

    return subObj;
}

总结

  • 本文主要考验正则表达式的使用。
  • 遗留问题:左右联动还未实现(左边滚动的时候,右边可以跟着滚动,类似markdown编辑器的效果)。
  • 正则表达式可视化测试地址:https://regexr-cn.com/
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-11-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题描述
  • 指定拆分规则
  • 拆分题目
  • 题目字符串转为对象
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档