POI替换docx文件中的文本段的关键字


prtyaa
prtyaa 2024-01-02 16:24:10 66031 赞同 0 反对 0
分类: 资源
POI替换docx文件中的文本段的关键字 需求描述 需要在替换文本段中的关键字的同时还是需要保留该关键字的格式

例如:

 

 

期望的结果:

 

 

问题点

文本段在POI中是使用XWPFParagraph 存储的,而XWPFParagraph 又是通过保存了一个XWPFRun的列表来存储每个子文本段的,同时XWPFRun 也负责每个子文本段的格式。一段XWPFParagraph被拆分为多少个XWPFRun,这个由Word编辑器自己决定,因此可能就会存在关键字部分被拆分到多个XWPFRun中,因此如果直接通过获取XWPFParagraph 的整体的文本,再替换关键字,会导致丢失掉了关键字部分的格式。

例如,上图中的内容在程序中XWPFParagraphXWPFRun列表为:

 

 

实现

因此需要自己编写一个多个子字符串组成的字符串列表的关键字搜索算法,并能返回关键字所在的列表的下标。代码如下:

查找算法:

private static void replaceInParagraph(XWPFParagraph para, Map<String, Object> parameterMap) {
        String paraText = para.getParagraphText();
        if(!paraText.contains("${") || !paraText.contains("}") ){
            return ;
        }
        log.info("paraText:【{}】",paraText);
        List<XWPFRun> xwpfRuns  = para.getRuns();
        StringBuilder keySb = new StringBuilder();
        boolean hasKey = false;
        LinkedHashMap<String,XmlObject> newParaMap = new LinkedHashMap<>();
        for(int i=0;i<xwpfRuns.size();i++){
            XWPFRun run = xwpfRuns.get(i);
            String str = run.text();
            int bIndex =0;
            int eIndex =0;
            do{
                int end = str.indexOf("}",eIndex);
                //已经有关键字内容
                if(hasKey){
                    if(end>=0) { //末尾
                        keySb.append(str.substring(bIndex,end));
                        eIndex = end + 1;
                        bIndex = eIndex;
                        String value = String.valueOf(parameterMap.getOrDefault(keySb.toString(),""));
                        //替换关键字内容
                        newParaMap.put(value,run.getCTR().copy());
                        keySb.setLength(0);
                        hasKey = false;
                        continue;
                    }else{
                        //有内容,但没有结束符,当前部分字符串作为关键字内容
                        keySb.append(str.substring(bIndex));
                        bIndex = str.length();
                        eIndex = bIndex;
                    }
                }else{
                    //没有关键字内容
                    int begin = str.indexOf("$",eIndex);
                    if(end>=0 && end<begin){
                        //修正无效的结束符
                        newParaMap.put(str.substring(bIndex,end+1),run.getCTR().copy());
                        eIndex = end + 1;
                        bIndex = eIndex;
                        continue;
                    }
                    //存在起始符
                    if(begin>=0){
                        int begin1 = str.indexOf("${",begin);
                        //存在关键字内容起始符
                        if(begin1>=0){
                            //保存关键字起始符
                            hasKey = true;
                            bIndex = begin1+2;
                            eIndex = bIndex ;
                            continue;
                        }else{
                            //不存在关键字内容起始符,
                            //$ 是当前字符串末尾且不是数组最后一个元素
                            if(begin==str.length()-1 && i+1<xwpfRuns.size()){
                                int begin2 = xwpfRuns.get(i+1).text().indexOf("{");
                                // { 必须是下一个字符串元素的首字母
                                if(begin2==0){
                                    hasKey = true;
                                    i++;
                                    run = xwpfRuns.get(i);
                                    str = run.text();
                                    bIndex=1;
                                    eIndex=1;
                                    continue;
                                }else{
                                    //下一个数组元素的第一个字符不是{
                                    newParaMap.put(str.substring(bIndex),run.getCTR().copy());
                                    break;
                                }
                            }else{
                                //不是最后一个字符或者是最后一个元素
                                newParaMap.put(str.substring(bIndex),run.getCTR().copy());
                                break;
                            }
                        }
                    }else{
                        //不存在起始符
                        newParaMap.put(str.substring(bIndex),run.getCTR().copy());
                        break;
                    }
                }
            }while(bIndex<str.length());
        }

        replaceParagraph(para,newParaMap);
        log.info("replaced  paraText:【{}】",para.getParagraphText());
    }

替换算法:

private static void replaceParagraph(XWPFParagraph para,LinkedHashMap<String,XmlObject> newParaMap){
        if(CollectionUtils.isNotEmpty(para.getRuns())) {
            //删除所有文本段
            while(para.removeRun(0)) {
            }
        }
        newParaMap.forEach((k,v)->{
            //创建新文本段,并恢复格式
            XWPFRun run = para.createRun();
            run.getCTR().set(v);
            run.setText(k,0);
        });
    }

总结

一开始想使用编译原理中的源码解析的方法处理,但后来发现这里token的关键字部分也可能被拆分,因此自己写了一个新的算法。 目前存在的问题是对于${key1$${key1}} 这种嵌套的关键字处理有争议,目前识别为key1$${key1},实际可能要识别成`${key1$ + key1 +}

如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!

评价 0 条
prtyaaL3
粉丝 1 资源 1949 + 关注 私信
最近热门资源
麒麟系统版本介绍白皮书  511
MiSans 阿拉伯语字体文件  452
解决新版本麒麟系统中微信打开白屏显示  395
麒麟系统进行系统监控,查看进程的运行时间来优化性能  328
临时关闭swap分区与永久关闭swap分区(注意必须确保系统有足够内存运行!)  219
统信桌面专业版添加字体  212
统信uos单一程序黑屏,任务栏正常显示解决办法  211
统信uos快捷键文档  183
统信系统双无线网卡设置关闭开启单一网卡  145
分享一个磁盘恢复工具,适用于多平台(包括统信)  119
最近下载排行榜
麒麟系统版本介绍白皮书 0
MiSans 阿拉伯语字体文件 0
解决新版本麒麟系统中微信打开白屏显示 0
麒麟系统进行系统监控,查看进程的运行时间来优化性能 0
临时关闭swap分区与永久关闭swap分区(注意必须确保系统有足够内存运行!) 0
统信桌面专业版添加字体 0
统信uos单一程序黑屏,任务栏正常显示解决办法 0
统信uos快捷键文档 0
统信系统双无线网卡设置关闭开启单一网卡 0
分享一个磁盘恢复工具,适用于多平台(包括统信) 0
作者收入月榜
1

prtyaa 收益399.62元

2

zlj141319 收益236.11元

3

IT-feng 收益219.61元

4

1843880570 收益214.2元

5

风晓 收益208.24元

6

哆啦漫漫喵 收益204.5元

7

777 收益173.07元

8

Fhawking 收益106.6元

9

信创来了 收益106.03元

10

克里斯蒂亚诺诺 收益91.08元

请使用微信扫码

添加我为好友,拉您入交流群!

请使用微信扫一扫!