问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

nodejs实现一个word文档解析器思路详解

发布网友 发布时间:2022-04-21 08:45

我来回答

1个回答

热心网友 时间:2022-04-20 16:21

之前项目里遇到一个需求,需要前端上传一个word文档,然后后端提取出该文档的指定位置的内容并保存。这里后端用的是nodejs,开始接到这个需求,发现无从下手,主要是没有处理过word这种类型的文档,怎么解析?
Excel倒是有相关的库可以用,而且很简单
思路
搜索了好一会儿,在npm上发现了一个叫做
adm-zip
的包,这个包可以解压缩word文档,原来word文档也是可以解压缩的,之前一直不知道,通过如下代码就可以将word文档解压缩,并进一步提取内容
var
admZip
=
require('adm-zip');
const
zip
=
new
admZip('test.docx');
//将该docx解压到指定文件夹result下
zip.extractAllTo("./result",
/*overwrite*/true);
首先我们新建一个docx文档,内容如下

然后运行上述代码进行解压缩,得到如下的文件,由下图可以看出生成了好几个文件夹,word的内容其实是在word文件夹里的document.xml文件内(这里解压缩后其实源文件还在,并没有消失)

进入word文件夹后的内容

我们继续打开document.xml文件来一探究竟里面到底是啥?注意要用浏览器直接打开,如果用ide打开显示出的所有内容都在一行,无法阅读!

上图只是word文档的一部分,会发现word文档内看着只有几段文字,但是xml中却是长篇大论,仔细分析下也很正常,xml全称可扩展标记语言,其被设计为传输和存储数据,它仅仅是一个纯文本的表示,而word中内容格式千变万化,肯定需要一种方法来有效描述这些内容的格式,因此采用了xml来描述
我们尝试一下将
测试文档
四个字加粗变色倾斜字体,如下图

然后再进行解压缩,得到docuemnt.xml并查看对应的内容,如下

这就很明显了,
<w:b/>
表示文字加粗,
<w:i/>
表示文字倾斜,
<w:color>
表示文字的颜色,所以这么4个字就需要这几行xml来描述,因此长篇大论的xml也就不足为奇
提取内容
上面说到了xml仅仅是一个文本的表示,我们可以用如下代码读取整个xml的内容,结果是一个
string
var
contentXml
=
zip.readAsText("word/document.xml");
接下来是重点,如何提取我们想要的内容呢,答案是正则表达式,首先我们得分析一下word文档的结构,word文档其实是由叫做
Paragraph
的段落所构成,在vb中可以很轻松的获取并修改段落,官网传送门点此

那么到底怎么样才是一个
Paragraph
呢,其实很简单,仔细观察word文档,见到下图中的小箭头了么,每个小箭头前面的内容就是一个段落,那么下图中一共有16个
Paragraph
,当然有些段落是空的,没有任何内容

我们再来研究xml的结构,收起展开的xml,如下图,发现
<w:p></w:p>
这么个标签就是表示的一个段落,中间还有些
<w:p>
藏在表格内,这么一看表格前面3个段落,后面3个段落,和上图是对应的

因此,
我们就可以提取出每个段落的文本并返回一个数组,每一项就是一个段落的内容
,这样就能够完整的解析出整个word的内容,关键在于如何提取每个
<w:p>
的内容,我们继续展开一个
<w:p>
进行观察,如下图,发现内容虽多,其实文本都保存在
<w:t>
中间,因此思路就清晰了,
首先用正则表达式提取出所有<w:p>的内容,再针对每个<w:p>的内容,进行进一步正则提取,提取出其里面所有<w:t>的内容,并拼接在一起构成一个段落的总内容

具体代码
下面是具体的提取代码
//参数是word文件名,第二个参数是回调表示解析完成
var
parser
=
function
parseWordDocument(absoluteWordPath,callback){
//返回内容的数组
var
resultList
=
[];
//如果文件存在
fs.exists(absoluteWordPath,
function(exists){
if(exists){
//解压缩
const
zip
=
new
admZip(absoluteWordPath);
//将document.xml(解压缩后得到的文件)读取为text内容
var
contentXml
=
zip.readAsText("word/document.xml");
//正则匹配出对应的<w:p>里面的内容,方法是先匹配<w:p>,再匹配里面的<w:t>,将匹配到的加起来即可
//注意?表示非贪婪模式(尽可能少匹配字符),否则只能匹配到一个<w:p></w:p>
var
matchedWP
=
contentXml.match(/<w:p.*?>.*?<\/w:p>/gi);
//继续匹配每个<w:p></w:p>里面的<w:t>,这里必须判断matchedWP存在否则报错
if(matchedWP){
matchedWP.forEach(function(wpItem){
//注意这里<w:t>的匹配,有可能是<w:t
xml:space="preserve">这种格式,需要特殊处理
var
matchedWT
=
wpItem.match(/(<w:t>.*?<\/w:t>)|(<w:t\s.[^>]*?>.*?<\/w:t>)/gi);
var
textContent
=
'';
if(matchedWT){
matchedWT.forEach(function(wtItem){
//如果不是<w:t
xml:space="preserve">格式
if(wtItem.indexOf('xml:space')===-1){
textContent+=wtItem.slice(5,-6);
}else{
textContent+=wtItem.slice(26,-6);
}
});
resultList.push(textContent)
}
});
//解析完成
callback(resultList)
}
}else{
callback(resultList)
}
});
};
注意一下如果段落前有空格,那么
<w:t>
的格式是不同的,如下,多了这个space描述,所以需要特殊处理
代码量其实很少,关键在于正则的编写,上述docx文档提取后的输出结果如下

最后我把这个工具写成了一个npm包,地址点这里
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
备忘录在手机的哪里 刚性消费有哪些 中国经济快速增长的原因 什么是刚性消费 什么叫刚性增长 特别精辟的个性签名(非常经典的个性句子) 特别经典的个性签名(非常惊艳的个性句子) 文艺范十足的个性签名(温柔治愈的个性签名句子) wps文字怎么设置每页头和尾 27岁的女人需要补充哪些营养元素 100求解:javascript读简单的xml文件 nodejs 如何生成xml文件 自动化专业 就业好吗 电气工程及其自动化怎么烂的专业为什么要分那么高 电气工程及其自动化专业以后出来工作可以干什么 3个自动化专业容易混淆,机械+电气+自动化,选不好就“坑”了 学自动化专业毕业后可以从事什么工作,有前途吗 机械设计制造及其自动化工资真的很低吗 404 Not Found 自动化专业的一些问题 Thu电气工程及其自动化是个坑吗 电气工程及其自动化专业真的很坑吗? 请教 :自动化专业,是不是很没前途? 自动化这个专业是不是不好? 自动化这个专业怎么样? 自动化专业好吗? 千万别学自动化专业?自动化专业现在吃香吗?出来都干什么? 自动化是不是一个很坑的专业 电气自动化这个专业怎么样,坑吗 都说自动化是垃圾专业,怎么办 nodejs调用java代码 其中java写的方法要传入一个枚举类型的参数,nodejs有枚举类型吗? 如何快速开发一个基于nodejs的会员管理系统 Python和nodeJS哪个更适合做爬虫? 如何使用Visual Studio 2013开发nodejs 客户端JavaScript 是否有权限读取用户硬盘上的本地文件 如何将xml文件转成word文档? php和node.js 如何使用nodejs做爬虫程序 nodejs怎么读取log文件内容 node.js在什么工具上有提示 nodejs-xlsx导出的excel,office打开提示“发现不可读取的内容”? 使用ant打包web的build.xml一定要自己写吗 什么求组词 nodejs可以模拟浏览器加载页面(包括页面上的js,css等)吗? 如何发挥NodeJS单线程异步非阻塞I/O性能优势 做梦梦到一只青蛙预示着什么 自己梦见他人,和我说他梦见青蛙和我笑啥意思? 梦见会说话的青蛙从水中跳出来,跟我很友好,我还跟它一起吃东西,它还帮助我做一些事情,我还抱着它, word中如何在表格中把字打到中央? 怎么用word文档把字打到a4纸大