解析word⽂件的简单实现
⼀、了解word结构
⽂章:、
office 97-03
office 97-03的存储规范为OLE。它是COM对象的⼦集,是⼀种对象链接和嵌⼊的技术,该技术可以包含⽂本,图形,电⼦表格甚⾄其他⼆进制数据。
OLE对象由对象头(ObjectHeader)和数据流(ObjectStream)组成。
(1)对象头各字段解析内容如下:
数据解释
01050000OLE Version
02000000Format ID
09000000ProgName Size(0x09)
4f4c45324c696e6b00ProgName (OLE2Link)
000a0000Data Size
(2)对象头和数据流通过d0cf11e0a1b11ae1分隔
xml文件怎么打开
(3)解析数据流的时候需要将ascii转换成hex
office 07-*
Doc⽂件的格式规范为OpenXML(OOXML),是微软在Office 2007中提出的⼀种新的⽂档格式。
Office 2007中的Word、Excel、PowerPoint默认均采⽤OpenXML格式。OpenXML在2006年12⽉成为了ECMA规范的⼀部分,编号为ECMA376;并于2008年4⽉通过国际标准化组织的表决,并于两个⽉后公布为ISO/IEC 29500国际标准。
Doc⽂件实际上⼀个压缩包,可以解压为⽬录。⽬录结构如下:
_rels⽂件夹:存放了所有指定的rels⽂件
docProps⽂件夹:存放了docx⽂档的主要属性信息
word⽂件夹:存放了docx⽂档的具体内容
[Content_Types].xml:描述的是整个⽂档内容的类型,把各个xml⽂件组合成⼀个整体
⼆、初步探索
1. 新建word⽂档,输⼊⼀些内容(最好是有规律,易辨识的内容):正⽂、⽂本框、表格、在第⼆页再输⼊正⽂
2. 修改word后缀为zip,解压缩后会发现⽂件格式确实如第⼀节中所述
3. 打开每个xml⽂件,搜索在第1步输⼊的内容,寻内容与xml的对应关系。
⼤致可以得出结论:word的主要内容存在l中。如果只要解析正⽂中的内容,直接解析这个xml⽂件即可。如果想页眉和页脚的⽂字内容,则需要另外解析l和l等⽂件。
三、解析l⽂件,读取doc中的⽂字
⽂字可能存在于正⽂(段落)、表格、⽂本框等位置。
正⽂(段落)中的⽂字直接读取XMLStreamConstants.CHARACTERS类型的内容即可获取。
但是如果⽂档中混合了表格和⽂本框的元素,则直接获取会出现以下问题:
1. 表格会失去形状,表格中每个单元格的⽂本都会占⼀⾏
2. ⽂本框中的⽂本会多次出现,原因是⾼版本的docx⽂件为了兼容低版本的word把⽂本框中的内容存了多份
问题1的解决⽅法:
根据结束标签的不同,执⾏不同的动作:
正⽂中的段落结束标记:打印内容
表格中单元格结束标记:追加\t
表格中⾏结束标记:追加\n
表格结束标记:打印内容
问题2的解决⽅法:
1. 当遇到不希望解析的节点的开始标签时,开启屏蔽标记
2. 开启屏蔽标记之后,不在收集内容节点
3. 直到遇到该节点的结束标签时,关闭屏蔽标记
四、完整代码
l.namespace.QName;
l.stream.XMLEventReader;
l.stream.XMLInputFactory;
l.stream.XMLStreamConstants;
l.stream.XMLStreamException;
l.stream.events.Characters;
l.stream.events.EndElement;
l.stream.events.StartElement;
l.stream.events.XMLEvent;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/
**
* 解析word⽂档,打印出⽂件中的内容(⽬前只⽀持2010版word⽂件)
* <p>
* ⽀持正⽂(段落)、表格、⽂本框
* <p>
* 注:表格打印后仍然可以保持基本形状
*/
public class ParseWord {
/**
* 是否开启调试
*/
private static boolean DEBUG =false;
public static void main(String[] args)throws IOException {
File docDir =unZipDocFiles("src/test.docx");
parseDocumentXml(docDir);
}
/**
* 解压doc⽂件到同⽬录下
* 解压doc⽂件到同⽬录下
*
* @param docPath doc⽂件路径
* @return 返回doc解压后的⽬录⽂件
*/
private static File unZipDocFiles(String docPath)throws IOException {
String descDir = docPath.substring(0, docPath.lastIndexOf("."));
return unZipFiles(docPath, descDir);
}
/**
* 解压zip⽂件
*
* @param zipPath zip⽂件路径
* @param descDir 解压⽬录
* @return 返回解压后的⽬录⽂件
*/
private static File unZipFiles(String zipPath, String descDir)throws IOException {
ZipFile zip =new ZipFile(new File(zipPath), Charset.forName("GBK"));// 解决中⽂⽂件夹乱码
File pathFile =new File(descDir);
if(!ists()){
pathFile.mkdirs();
}
for(Enumeration<?extends ZipEntry> entries = ies(); entries.hasMoreElements();){
ZipEntry entry =(ZipEntry) Element();
String zipEntryName = Name();
InputStream in = InputStream(entry);
String outPath =(descDir +"/"+ zipEntryName);
// 判断路径是否存在,不存在则创建⽂件路径
File file =new File(outPath.substring(0, outPath.lastIndexOf('/')));
if(!ists()){
file.mkdirs();
}
// 判断⽂件全路径是否为⽂件夹,如果是上⾯已经上传,不需要解压
if(new File(outPath).isDirectory()){
continue;
}
FileOutputStream out =new FileOutputStream(outPath);
byte[] buf1 =new byte[1024];
int len;
while((len = in.read(buf1))>0){
out.write(buf1,0, len);
}
in.close();
out.close();
}
return pathFile;
}
/**
* 解析l
*
* @param docDir doc解压后的⽬录⽂件
*/
private static void parseDocumentXml(File docDir){
try{
String documentXmlPath = Path()+"/l";
XMLInputFactory factory = wInstance();
XMLEventReader eventReader = ateXMLEventReader(new FileReader(documentXmlPath));
// 屏蔽标记
boolean maskFlag =false;
// 表格内标记
boolean inTable =false;
StringBuffer s =new StringBuffer();
while(eventReader.hasNext()){
XMLEvent event = Event();
debug(event);
EventType()){
case XMLStreamConstants.START_ELEMENT:
StartElement startElement = event.asStartElement();
if(Name())){
// 屏蔽不想要的标签
maskFlag =true;
}else{
if("tbl".Name().getLocalPart())){
// 当遇到tbl标签时表⽰进⼊表格
inTable =true;
}
}
break;
case XMLStreamConstants.CHARACTERS:
Characters characters = event.asCharacters();
if(!maskFlag){
s.Data());
debug("=="+ Data());
}
break;
case XMLStreamConstants.END_ELEMENT:
EndElement endElement = event.asEndElement();
if(!maskFlag){
Name().getLocalPart()){
case"p":
if(!inTable){
// 当p标签不在表格中时直接打印
System.out.println(s);
s =new StringBuffer();
}
break;
case"tc":
// 当遇到tc闭合标签时,表⽰表格中的单元格结束
s.append("\t");
break;
case"tr":
// 当遇到tc闭合标签时,表⽰表格中的⾏结束
s.append("\n");
break;
case"tbl":
// 当遇到tc闭合标签时,表⽰表格结束
System.out.println(s);
s =new StringBuffer();
inTable =false;
break;
}
}
if(Name())){
// 退出屏蔽标记
maskFlag =false;
}
break;
}
}
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(XMLStreamException e){
e.printStackTrace();