導航:首頁 > 文件類型 > word文件解析

word文件解析

發布時間:2023-06-12 08:45:29

Ⅰ nodejs實現一個word文檔解析器思路詳解

之前項目里遇到一個需求,需要前端上傳一個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包,地址點這里

Ⅱ word可以解析html嗎

word可以解析html。具體方法如下:
1、使用墨子界面上左上角的文件,在展開的頁面中選擇打開。
2、在其中的打開界面里選擇所需要的html個鍵,解析即可。

java:解析word文檔(前程無憂簡歷),最好有代碼案例poi或者jacob解析都可以,有jar資源,求急。感謝

poi讀取前程無憂的簡歷會打不開的,至少我以前讀是這樣的,因為他有時候是mht文件直接另存為word文檔的,所以保險起見建議用jacob來讀,如果他是doc或者是docx文檔可以轉化為html然後用jsoup來讀取,效果挺好的

下面是轉化的代碼:

packagecom.java.doc;
importcom.jacob.activeX.ActiveXComponent;
importcom.jacob.com.Dispatch;
importcom.jacob.com.Variant;
publicclassJacobRead{
publicstaticvoidextractDoc(StringinputFIle,StringoutputFile){
booleanflag=false;

//打開Word應用程序
ActiveXComponentapp=newActiveXComponent("Word.Application");
try{
//設置word不可見
app.setProperty("Visible",newVariant(false));
//打開word文件
Dispatchdoc1=app.getProperty("Documents").toDispatch();
Dispatchdoc2=Dispatch.invoke(
doc1,
"Open",
Dispatch.Method,
newObject[]{inputFIle,newVariant(false),
newVariant(true)},newint[1]).toDispatch();
//作為txt格式保存到臨時文件
Dispatch.invoke(doc2,"SaveAs",Dispatch.Method,newObject[]{
outputFile,newVariant(7)},newint[1]);
//關閉word
Variantf=newVariant(false);
Dispatch.call(doc2,"Close",f);
flag=true;
}catch(Exceptione){
e.printStackTrace();
}finally{
app.invoke("Quit",newVariant[]{});
}
if(flag==true){
System.out.println("TransformedSuccessfully");
}else{
System.out.println("TransformFailed");
}
}

publicstaticvoidmain(String[]args){


JacobRead.extractDoc("D:/xxxx簡歷.doc","D:/e.txt");
}
}

當然,也可以轉化為txt讀取,這部分代碼沒保存,你可以到網上找找,和轉化成html的方法大差不差。

然後下面是我以前寫的poi讀取的方式:

packageTestHanLp;

importjava.io.FileInputStream;
importjava.io.FileNotFoundException;
importjava.io.IOException;

importorg.apache.poi.POIXMLDocument;
importorg.apache.poi.POIXMLTextExtractor;
importorg.apache.poi.hwpf.extractor.WordExtractor;
importorg.apache.poi.openxml4j.opc.OPCPackage;
importorg.apache.poi.xwpf.extractor.XWPFWordExtractor;
importorg.apache.poi.xwpf.usermodel.XWPFDocument;


publicclassTest{

privatestaticStringtext="";
publicstaticStringRead(Stringpath)throwsException{
//解析docx格式的簡歷
if(path.toLowerCase().endsWith("docx")){

try{
OPCPackageoPCPackage=POIXMLDocument.openPackage(path);
XWPFDocumentxwpf=newXWPFDocument(oPCPackage);
POIXMLTextExtractorex=newXWPFWordExtractor(xwpf);
text=ex.getText();
oPCPackage.close();
}
catch(FileNotFoundExceptione)
{
e.printStackTrace();
}
catch(IOExceptione)
{
e.printStackTrace();
}

}else{

//解析doc格式的簡歷
if(path.toLowerCase().endsWith("doc")){
FileInputStreamfis=newFileInputStream(path);//載入文檔
WordExtractorwordExtractor=newWordExtractor(fis);
String[]paragraph=wordExtractor.getParagraphText();
StringBufferstringBuffer=newStringBuffer();
for(inti=0;i<paragraph.length;i++){
if(null!=paragraph[i]&&!"".equals(paragraph[i])){
paragraph[i]=paragraph[i].substring(0,paragraph[i].length()-1);//去掉末尾符號
}
stringBuffer.append(paragraph[i]).append(" ");//將每一小段隔開
}
text=stringBuffer.toString();
}
}
returntext;
}
}

望題主採納

對了,jacob讀取word文檔的效果比poi號,但運行速度不夠,用的時候自己考慮考慮吧

Ⅳ word文檔出現文件無法解析是怎麼回事

肯定是編輯的時候用的word版本比較高 但是打開的那個word版本比較低,有些個文件解析不出來,出現亂碼!

Ⅳ java解析word文檔有哪些方法

java讀取word文檔時,雖然網上介紹了很多插件poi、java2Word、jacob、itext等等,poi無法讀取格式(新的估
計行好像還在處於研發階段,不太穩定,做項目不太敢用);java2Word、jacob容易報錯找不到注冊,比較詭異,我曾經在不同的機器上試過,操作
方法完全一致,有的機器不報錯,有的報錯,去他們論壇找高人解決也說不出原因,項目部署用它有點玄;itxt好像寫很方便但是我查了好久資料沒有見到過關
於讀的好辦法。經過一番選擇還是折中點採用rtf最好,畢竟rtf是開源格式,不需要藉助任何插件,只需基本IO操作外加編碼轉換即可。rtf格式文件表
面看來和doc沒啥區別,都可以用word打開,各種格式都可以設定。

----- 實現的功能:讀取rtf模板內容(格式和文本內容),替換變化部分,形成新的rtf文檔。

----- 實現思路:模板中固定部分手動輸入,變化的部分用$info$表示,只需替換$info$即可。

1、採用位元組的形式讀取rtf模板內容

2、將可變的內容字元串轉為rtf編碼

3、替換原文中的可變部分,形成新的rtf文檔

主要程序如下:

public String bin2hex(String bin) {

char[] digital = "0123456789ABCDEF".toCharArray();

StringBuffer sb = new StringBuffer("");

byte[] bs = bin.getBytes();

int bit;

for (int i = 0; i < bs.length;i++) {

bit = (bs[i] & 0x0f0)
>> 4;

sb.append("\\'");

sb.append(digital[bit]);

bit = bs[i] & 0x0f;

sb.append(digital[bit]);

}

return sb.toString();

}

public String readByteRtf(InputStream ins, String path){

String sourcecontent =
"";

try{

ins = new
FileInputStream(path);

byte[] b
= new byte[1024];

if (ins == null) {

System.out.println("源模板文件不存在");

}

int bytesRead = 0;

while (true) {

bytesRead = ins.read(b, 0, 1024); // return final read bytes
counts

if(bytesRead == -1) {// end of InputStream

System.out.println("讀取模板文件結束");

break;

}

sourcecontent += new String(b, 0, bytesRead); // convert to string
using bytes

}

}catch(Exception e){

e.printStackTrace();

}

return sourcecontent ;

}

以上為核心代碼,剩餘部分就是替換,從新組裝java中的String.replace(oldstr,newstr);方法可以實現,在這就不貼了。源代碼部分詳見附件。

運行源代碼前提:

c盤創建YQ目錄,將附件中"模板.rtf"復制到YQ目錄之下,運行OpreatorRTF.java文件即可,就會在YQ目錄下生成文件名如:21時15分19秒_cheney_記錄.rtf
的文件。

package com;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileWriter;

import java.io.IOException;

import java.io.InputStream;

import java.io.PrintWriter;

import java.text.SimpleDateFormat;

import java.util.Date;

public class OperatorRTF {

public String strToRtf(String content){

char[] digital = "0123456789ABCDEF".toCharArray();

StringBuffer sb = new StringBuffer("");

byte[] bs = content.getBytes();

int bit;

for (int i = 0; i < bs.length; i++) {

bit = (bs[i] & 0x0f0)
>> 4;

sb.append("\\'");

sb.append(digital[bit]);

bit = bs[i] & 0x0f;

sb.append(digital[bit]);

}

return sb.toString();

}

public String replaceRTF(String content,String replacecontent,int
flag){

String rc = strToRtf(replacecontent);

String target = "";

if(flag==0){

target = content.replace("$timetop$",rc);

}

if(flag==1){

target = content.replace("$info$",rc);

}

if(flag==2){

target = content.replace("$idea$",rc);

}

if(flag==3){

target = content.replace("$advice$",rc);

}

if(flag==4){

target = content.replace("$infosend$",rc);

}

return target;

}

public String getSavePath() {

String path = "C:\\YQ";

File fDirecotry = new File(path);

if (!fDirecotry.exists()) {

fDirecotry.mkdirs();

}

return path;

}

public String ToSBC(String input){

char[] c =
input.toCharArray();

for (int i =
0; i < c.length; i++){

if (c[i] == 32){

c[i] = (char) 12288;

continue;

}

if (c[i] < 127){

c[i] = (char) (c[i] + 65248);

}

}

return new
String(c);

}

public void rgModel(String username, String content) {

// TODO Auto-generated method stub

Date current=new Date();

SimpleDateFormat sdf=new java.text.SimpleDateFormat("yyyy-MM-dd
HH:mm:ss");

String targetname = sdf.format(current).substring(11,13) + "時";

targetname += sdf.format(current).substring(14,16) + "分";

targetname += sdf.format(current).substring(17,19) + "秒";

targetname += "_" + username +"_記錄.rtf";

String strpath = getSavePath();

String sourname = strpath+"\\"+"模板.rtf";

String sourcecontent = "";

InputStream ins = null;

try{

ins = new FileInputStream(sourname);

byte[] b = new byte[1024];

if (ins == null) {

System.out.println("源模板文件不存在");

}

int bytesRead = 0;

while (true) {

bytesRead = ins.read(b, 0, 1024); // return final read bytes
counts

if(bytesRead == -1) {// end of InputStream

System.out.println("讀取模板文件結束");

break;

}

sourcecontent += new String(b, 0, bytesRead); // convert to string
using bytes

}

}catch(Exception e){

e.printStackTrace();

}

String targetcontent = "";

String array[] = content.split("~");

for(int i=0;i<array.length;i++){

if(i==0){

targetcontent = replaceRTF(sourcecontent, array[i], i);

}else{

targetcontent = replaceRTF(targetcontent, array[i], i);

}

}

try {

FileWriter fw = new FileWriter(getSavePath()+"\\" +
targetname,true);

PrintWriter out = new PrintWriter(fw);

if(targetcontent.equals("")||targetcontent==""){

out.println(sourcecontent);

}else{

out.println(targetcontent);

}

out.close();

fw.close();

System.out.println(getSavePath()+" 該目錄下生成文件" +
targetname + " 成功");

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

public static void main(String[] args) {

// TODO Auto-generated method stub

OperatorRTF oRTF = new OperatorRTF();

String content =
"2008年10月12日9時-2008年10月12日6時~我們參照檢驗葯品的方法~我們參照檢驗葯品的方法~我們參照檢驗葯品的方法~我們參照檢驗葯品的方法";

oRTF.rgModel("cheney",content);

}

}

閱讀全文

與word文件解析相關的資料

熱點內容
迷你編程登錄迷你號驗證碼是什麼 瀏覽:398
做數據表如何打出平方 瀏覽:447
在vmos下載的文件路徑在哪 瀏覽:771
有什麼購物app是用微信支付的 瀏覽:99
數控編程中夾持什麼意思 瀏覽:295
文件夾能容納多少張截圖 瀏覽:85
視頻文件查找 瀏覽:786
如何進入java的編程界面 瀏覽:371
二級開發者還有哪些app 瀏覽:241
app充值請聯系itunes 瀏覽:678
矢量app和cdr哪個好 瀏覽:85
系統文件壞了如何修復 瀏覽:20
鍵盤系統文件誤刪 瀏覽:738
白金英雄壇所有版本 瀏覽:842
ps文件轉hsj 瀏覽:382
哪個網站電影 瀏覽:490
ps4游戲文件格式名稱 瀏覽:290
caxa教程2007 瀏覽:832
新點是什麼小說網站 瀏覽:753
魔獸世界冰封王座3版本轉換器 瀏覽:418

友情鏈接