溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

nodejs實現(xiàn)一個word文檔解析器思路詳解

發(fā)布時間:2020-09-08 17:27:59 來源:腳本之家 閱讀:216 作者:超級索尼子 欄目:web開發(fā)

之前項目里遇到一個需求,需要前端上傳一個word文檔,然后后端提取出該文檔的指定位置的內(nèi)容并保存。這里后端用的是nodejs,開始接到這個需求,發(fā)現(xiàn)無從下手,主要是沒有處理過word這種類型的文檔,怎么解析? Excel倒是有相關(guān)的庫可以用,而且很簡單

思路

搜索了好一會兒,在npm上發(fā)現(xiàn)了一個叫做 adm-zip 的包,這個包可以解壓縮word文檔,原來word文檔也是可以解壓縮的,之前一直不知道,通過如下代碼就可以將word文檔解壓縮,并進一步提取內(nèi)容

var admZip = require('adm-zip');
const zip = new admZip('test.docx');
//將該docx解壓到指定文件夾result下
zip.extractAllTo("./result", /*overwrite*/true);

首先我們新建一個docx文檔,內(nèi)容如下

nodejs實現(xiàn)一個word文檔解析器思路詳解 

然后運行上述代碼進行解壓縮,得到如下的文件,由下圖可以看出生成了好幾個文件夾,word的內(nèi)容其實是在word文件夾里的document.xml文件內(nèi)(這里解壓縮后其實源文件還在,并沒有消失)

nodejs實現(xiàn)一個word文檔解析器思路詳解 

進入word文件夾后的內(nèi)容

nodejs實現(xiàn)一個word文檔解析器思路詳解 

我們繼續(xù)打開document.xml文件來一探究竟里面到底是啥?注意要用瀏覽器直接打開,如果用ide打開顯示出的所有內(nèi)容都在一行,無法閱讀!

nodejs實現(xiàn)一個word文檔解析器思路詳解 

上圖只是word文檔的一部分,會發(fā)現(xiàn)word文檔內(nèi)看著只有幾段文字,但是xml中卻是長篇大論,仔細分析下也很正常,xml全稱可擴展標記語言,其被設(shè)計為傳輸和存儲數(shù)據(jù),它僅僅是一個純文本的表示,而word中內(nèi)容格式千變?nèi)f化,肯定需要一種方法來有效描述這些內(nèi)容的格式,因此采用了xml來描述

我們嘗試一下將 測試文檔 四個字加粗變色傾斜字體,如下圖

nodejs實現(xiàn)一個word文檔解析器思路詳解 

然后再進行解壓縮,得到docuemnt.xml并查看對應(yīng)的內(nèi)容,如下

nodejs實現(xiàn)一個word文檔解析器思路詳解 

這就很明顯了, <w:b/> 表示文字加粗, <w:i/> 表示文字傾斜, <w:color>
表示文字的顏色,所以這么4個字就需要這幾行xml來描述,因此長篇大論的xml也就不足為奇

提取內(nèi)容

上面說到了xml僅僅是一個文本的表示,我們可以用如下代碼讀取整個xml的內(nèi)容,結(jié)果是一個 string

var contentXml = zip.readAsText("word/document.xml");

接下來是重點,如何提取我們想要的內(nèi)容呢,答案是正則表達式,首先我們得分析一下word文檔的結(jié)構(gòu),word文檔其實是由叫做 Paragraph 的段落所構(gòu)成,在vb中可以很輕松的獲取并修改段落,官網(wǎng)傳送門點此

nodejs實現(xiàn)一個word文檔解析器思路詳解 

那么到底怎么樣才是一個 Paragraph 呢,其實很簡單,仔細觀察word文檔,見到下圖中的小箭頭了么,每個小箭頭前面的內(nèi)容就是一個段落,那么下圖中一共有16個 Paragraph ,當(dāng)然有些段落是空的,沒有任何內(nèi)容

nodejs實現(xiàn)一個word文檔解析器思路詳解 

我們再來研究xml的結(jié)構(gòu),收起展開的xml,如下圖,發(fā)現(xiàn) <w:p></w:p> 這么個標簽就是表示的一個段落,中間還有些 <w:p>

藏在表格內(nèi),這么一看表格前面3個段落,后面3個段落,和上圖是對應(yīng)的

nodejs實現(xiàn)一個word文檔解析器思路詳解 

因此, 我們就可以提取出每個段落的文本并返回一個數(shù)組,每一項就是一個段落的內(nèi)容 ,這樣就能夠完整的解析出整個word的內(nèi)容,關(guān)鍵在于如何提取每個 <w:p> 的內(nèi)容,我們繼續(xù)展開一個 <w:p> 進行觀察,如下圖,發(fā)現(xiàn)內(nèi)容雖多,其實文本都保存在 <w:t> 中間,因此思路就清晰了, 首先用正則表達式提取出所有<w:p>的內(nèi)容,再針對每個<w:p>的內(nèi)容,進行進一步正則提取,提取出其里面所有<w:t>的內(nèi)容,并拼接在一起構(gòu)成一個段落的總內(nèi)容

nodejs實現(xiàn)一個word文檔解析器思路詳解 

具體代碼

下面是具體的提取代碼

//參數(shù)是word文件名,第二個參數(shù)是回調(diào)表示解析完成
var parser = function parseWordDocument(absoluteWordPath,callback){
 //返回內(nèi)容的數(shù)組
 var resultList = [];
 //如果文件存在
 fs.exists(absoluteWordPath, function(exists){
 if(exists){
 //解壓縮
 const zip = new admZip(absoluteWordPath);
 //將document.xml(解壓縮后得到的文件)讀取為text內(nèi)容
 var contentXml = zip.readAsText("word/document.xml");
 //正則匹配出對應(yīng)的<w:p>里面的內(nèi)容,方法是先匹配<w:p>,再匹配里面的<w:t>,將匹配到的加起來即可
 //注意?表示非貪婪模式(盡可能少匹配字符),否則只能匹配到一個<w:p></w:p>
 var matchedWP = contentXml.match(/<w:p.*?>.*?<\/w:p>/gi);
 //繼續(xù)匹配每個<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描述,所以需要特殊處理

代碼量其實很少,關(guān)鍵在于正則的編寫,上述docx文檔提取后的輸出結(jié)果如下

nodejs實現(xiàn)一個word文檔解析器思路詳解 

最后我把這個工具寫成了一個npm包,地址點這里

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI