2007版本的excel使用ooxml规范 生成的xml文档格式,熟悉文档格式,才能解析正确
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:etc="http://www.wps.cn/officeDocument/2017/etCustomData">
<sheetPr/>
<dimension ref="A1:G20"/>
<sheetViews>
<sheetView workbookViewId="0">
<selection activeCell="F2" sqref="F2"/>
</sheetView>
</sheetViews>
<sheetFormatPr defaultColWidth="10" defaultRowHeight="14.4" outlineLevelCol="6"/>
<cols>
<col min="1" max="1" width="21.7777777777778" customWidth="1"/>
<col min="2" max="2" width="12.1111111111111" customWidth="1"/>
<col min="3" max="3" width="42.4722222222222" customWidth="1"/>
<col min="4" max="4" width="13.7037037037037" customWidth="1"/>
<col min="5" max="6" width="20.7592592592593" customWidth="1"/>
<col min="7" max="7" width="19.1296296296296" customWidth="1"/>
<col min="8" max="8" width="9.76851851851852" customWidth="1"/>
</cols>
<sheetData>
<row r="1" ht="66" customHeight="1" spans="1:6">
<c r="A1" s="1">
<v>0</v>
</c>
<c r="B1" s="1" t="s">
<v>0</v>
</c>
<c r="C1" s="1" t="s">
<v>1</v>
</c>
<c r="D1" s="1" t="s">
<v>2</v>
</c>
<c r="F1" s="1"/>
</row>
<row r="2" ht="60" customHeight="1" spans="2:6">
<c r="B2" s="1" t="s">
<v>3</v>
</c>
<c r="C2" s="1" t="s">
<v>4</v>
</c>
<c r="D2" s="1" t="s">
<v>5</v>
</c>
<c r="E2" s="1" t="s">
<v>6</v>
</c>
<c r="F2" t="s">
<v>7</v>
</c>
</row>
<row r="3" ht="61" customHeight="1" spans="1:7">
<c r="A3" s="1">
<v>0</v>
</c>
<c r="B3" s="1" t="s">
<v>8</v>
</c>
<c r="C3" s="1" t="s">
<v>9</v>
</c>
<c r="D3" s="1" t="s">
<v>9</v>
</c>
<c r="E3" s="1" t="s">
<v>10</v>
</c>
<c r="F3" s="1" t="s">
<v>11</v>
</c>
<c r="G3" s="1" t="s">
<v>12</v>
</c>
</row>
<row r="4" ht="57" customHeight="1" spans="3:3">
<c r="C4" s="2" t="s">
<v>13</v>
</c>
</row>
<row r="5" ht="25.6" customHeight="1" spans="1:6">
<c r="A5" s="1">
<v>0</v>
</c>
<c r="C5" s="3" t="s">
<v>14</v>
</c>
<c r="D5" s="3"/>
<c r="E5" s="3"/>
<c r="F5" s="3"/>
</row>
<row r="6" ht="16.55" customHeight="1" spans="1:7">
<c r="A6" s="1">
<v>0</v>
</c>
<c r="C6" s="4" t="s">
<v>15</v>
</c>
<c r="D6" s="4"/>
<c r="E6" s="4"/>
<c r="F6" s="4"/>
<c r="G6" s="4"/>
</row>
<row r="7" ht="19.9" customHeight="1" spans="1:7">
<c r="A7" s="1">
<v>0</v>
</c>
<c r="C7" s="5" t="s">
<v>16</v>
</c>
<c r="D7" s="11" t="s">
<v>17</v>
</c>
<c r="E7" s="11" t="s">
<v>18</v>
</c>
<c r="F7" s="12" t="s">
<v>19</v>
</c>
<c r="G7" s="12" t="s">
<v>20</v>
</c>
</row>
<row r="8" ht="19.9" customHeight="1" spans="1:7">
<c r="A8" s="1">
<v>0</v>
</c>
<c r="C8" s="8" t="s">
<v>21</v>
</c>
<c r="D8" s="13" t="s">
<v>22</v>
</c>
<c r="E8" s="14">
<v>60.111</v>
</c>
<c r="F8" s="14">
<v>0.01</v>
</c>
<c r="G8" s="15">
<v>60.101</v>
</c>
</row>
<row r="9" ht="19.9" customHeight="1" spans="1:7">
<c r="A9" s="1" t="s">
<v>23</v>
</c>
<c r="B9" s="1" t="s">
<v>24</v>
</c>
<c r="C9" s="16" t="s">
<v>25</v>
</c>
<c r="D9" s="17" t="s">
<v>26</v>
</c>
<c r="E9" s="18">
<v>20.1</v>
</c>
<c r="F9" s="19">
<v>0.005</v>
</c>
<c r="G9" s="20">
<v>20.095</v>
</c>
</row>
<row r="10" ht="19.9" customHeight="1" spans="1:7">
<c r="A10" s="1" t="s">
<v>23</v>
</c>
<c r="B10" s="1" t="s">
<v>27</v>
</c>
<c r="C10" s="8" t="s">
<v>28</v>
</c>
<c r="D10" s="13" t="s">
<v>29</v>
</c>
<c r="E10" s="14">
<v>40.011</v>
</c>
<c r="F10" s="14">
<v>0.005</v>
</c>
<c r="G10" s="15">
<v>40.006</v>
</c>
</row>
<row r="11" ht="19.9" customHeight="1" spans="1:7">
<c r="A11" s="1">
<v>0</v>
</c>
<c r="C11" s="21" t="s">
<v>30</v>
</c>
<c r="D11" s="22" t="s">
<v>31</v>
</c>
<c r="E11" s="23">
<v>300000</v>
</c>
<c r="F11" s="23">
<v>1300</v>
</c>
<c r="G11" s="24">
<v>298700</v>
</c>
</row>
<row r="12" ht="19.9" customHeight="1" spans="1:7">
<c r="A12" s="1" t="s">
<v>23</v>
</c>
<c r="B12" s="1" t="s">
<v>32</v>
</c>
<c r="C12" s="25" t="s">
<v>25</v>
</c>
<c r="D12" s="26" t="s">
<v>33</v>
</c>
<c r="E12" s="19">
<v>100000</v>
</c>
<c r="F12" s="19">
<v>500</v>
</c>
<c r="G12" s="20">
<v>99500</v>
</c>
</row>
<row r="13" ht="19.9" customHeight="1" spans="1:7">
<c r="A13" s="1" t="s">
<v>23</v>
</c>
<c r="B13" s="1" t="s">
<v>34</v>
</c>
<c r="C13" s="8" t="s">
<v>28</v>
</c>
<c r="D13" s="13" t="s">
<v>35</v>
</c>
<c r="E13" s="14">
<v>200000</v>
</c>
<c r="F13" s="14">
<v>800</v>
</c>
<c r="G13" s="15">
<v>199200</v>
</c>
</row>
<row r="14" ht="19.9" customHeight="1" spans="1:7">
<c r="A14" s="1">
<v>0</v>
</c>
<c r="C14" s="27" t="s">
<v>36</v>
</c>
<c r="D14" s="13" t="s">
<v>37</v>
</c>
<c r="E14" s="14">
<v>0</v>
</c>
<c r="F14" s="14">
<v>0</v>
</c>
<c r="G14" s="15">
<v>0</v>
</c>
</row>
<row r="15" ht="19.9" customHeight="1" spans="1:7">
<c r="A15" s="1" t="s">
<v>23</v>
</c>
<c r="B15" s="1" t="s">
<v>38</v>
</c>
<c r="C15" s="25" t="s">
<v>25</v>
</c>
<c r="D15" s="26" t="s">
<v>39</v>
</c>
<c r="E15" s="19">
<v>0</v>
</c>
<c r="F15" s="19">
<v>0</v>
</c>
<c r="G15" s="20">
<v>0</v>
</c>
</row>
<row r="16" ht="19.9" customHeight="1" spans="1:7">
<c r="A16" s="1" t="s">
<v>23</v>
</c>
<c r="B16" s="1" t="s">
<v>40</v>
</c>
<c r="C16" s="8" t="s">
<v>28</v>
</c>
<c r="D16" s="13" t="s">
<v>41</v>
</c>
<c r="E16" s="14">
<v>0</v>
</c>
<c r="F16" s="14">
<v>0</v>
</c>
<c r="G16" s="24">
<v>0</v>
</c>
</row>
<row r="17" ht="19.9" customHeight="1" spans="1:7">
<c r="A17" s="1">
<v>0</v>
</c>
<c r="C17" s="27" t="s">
<v>42</v>
</c>
<c r="D17" s="13" t="s">
<v>43</v>
</c>
<c r="E17" s="14">
<v>300060.111</v>
</c>
<c r="F17" s="14">
<v>3860.454646</v>
</c>
<c r="G17" s="24">
<v>296199.656354</v>
</c>
</row>
<row r="18" ht="19.9" customHeight="1" spans="1:7">
<c r="A18" s="1" t="s">
<v>23</v>
</c>
<c r="B18" s="1" t="s">
<v>44</v>
</c>
<c r="C18" s="25" t="s">
<v>25</v>
</c>
<c r="D18" s="26" t="s">
<v>45</v>
</c>
<c r="E18" s="19">
<v>100020.1</v>
</c>
<c r="F18" s="19">
<v>3059.571857</v>
</c>
<c r="G18" s="20">
<v>96960.528143</v>
</c>
</row>
<row r="19" ht="19.9" customHeight="1" spans="1:7">
<c r="A19" s="1" t="s">
<v>23</v>
</c>
<c r="B19" s="1" t="s">
<v>46</v>
</c>
<c r="C19" s="8" t="s">
<v>28</v>
</c>
<c r="D19" s="28" t="s">
<v>47</v>
</c>
<c r="E19" s="29">
<v>200040.011</v>
</c>
<c r="F19" s="29">
<v>800.882789</v>
</c>
<c r="G19" s="30">
<v>199239.128211</v>
</c>
</row>
<row r="20" ht="15.8" customHeight="1" spans="1:7">
<c r="A20" s="1">
<v>0</v>
</c>
<c r="C20" s="1" t="s">
<v>48</v>
</c>
<c r="D20" s="1"/>
<c r="E20" s="1"/>
<c r="F20" s="1"/>
<c r="G20" s="1"/>
</row>
</sheetData>
<mergeCells count="3">
<mergeCell ref="C5:F5"/>
<mergeCell ref="C6:G6"/>
<mergeCell ref="C20:G20"/>
</mergeCells>
<pageMargins left="0.75" right="0.75" top="0.26875" bottom="0.26875" header="0" footer="0"/>
<pageSetup paperSize="9" orientation="portrait"/>
<headerFooter/>
</worksheet>
//将ooxml格式的excel转化为opc规范格式
OPCPackage pkg = OPCPackage.open(file);
XSSFReader xssfReader = new XSSFReader(pkg);
//样式表
stylesTable = xssfReader.getStylesTable();
//共享字符串变量表
SharedStringsTable sst = xssfReader.getSharedStringsTable();
//获取解析器
XMLReader parser = XMLReaderFactory.createXMLReader("com.sun.org.apache.xerces.internal.parsers.SAXParser");
this.sst = sst;
parser.setContentHandler(this);
XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
while (sheets.hasNext()) { //遍历sheet
curRow = 1; //标记初始行为第一行
sheetIndex++;
InputStream sheet = sheets.next(); //sheets.next()和sheets.getSheetName()不能换位置,否则sheetName报错
sheetName = sheets.getSheetName();
InputSource sheetSource = new InputSource(sheet);
parser.parse(sheetSource); //解析excel的每条记录,在这个过程中startElement()、characters()、endElement()这三个函数会依次执行
sheet.close();
}
核心思想:
自定义Handler实现Defaulthandler,并重写其中的startElement,characters,endElement方法
class myHandler extends DefaultHandler{
//第一步
@Override
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
//重点使用
name:当前元素的XML 限定(前缀)名
attributes:当前元素节点的属性
//例:c表示单元格cell
if(name.equals("c")){...}
}
//第二步
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
//获取textNode中的值,该值在不同数据类型下,代表含义不同
//如当前单元格时字符串类型,则里面的值表示该单元格实际值在SST(共享字符串变量表)中的索引
//若当前单元格类型是数字类型,则表示实际数字值
value = new String(ch, start, length);
}
//第三步
@Override
public void endElement(String uri, String localName, String name) throws SAXException {
//跟开始节点相对应,xml中的元素都是成对出现的,有开始,有结束
}
}
其中的难点在与判断当前单元格的数据类型,并获取其实际值,以及空单元补全问题:
/**
* 单元格中的数据可能的数据类型
*/
enum CellDataType {
BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER, DATE, NULL
}
判断当前单元格类型的方法:
public void getCellDataType(Attributes attributes) {
nextDataType = CellDataType.NUMBER; //cellType为空,则表示该单元格类型为数字
formatIndex = -1;
formatString = null;
//通过XML 限定(前缀)名查找属性的值
String cellType = attributes.getValue("t"); //单元格类型
String cellStyleStr = attributes.getValue("s"); //
String columnData = attributes.getValue("r"); //获取单元格的位置,如A1,B1
if ("b".equals(cellType)) { //处理布尔值
nextDataType = CellDataType.BOOL;
} else if ("e".equals(cellType)) { //处理错误
nextDataType = CellDataType.ERROR;
} else if ("inlineStr".equals(cellType)) {//处理excel函数
nextDataType = CellDataType.INLINESTR;
} else if ("s".equals(cellType)) { //处理字符串
nextDataType = CellDataType.SSTINDEX;
} else if ("str".equals(cellType)) {//处理公式
nextDataType = CellDataType.FORMULA;
}
//excel中的日期类型较难处理,下面是其处理方式
if (cellStyleStr != null) {
int styleIndex = Integer.parseInt(cellStyleStr);
XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);
formatIndex = style.getDataFormat();
formatString = style.getDataFormatString();
//说明是日期类型
if (formatString.contains("m/d/yy")) {
nextDataType = CellDataType.DATE;
formatString = "yyyy-MM-dd hh:mm:ss";
}
//说明是NULL类型,我也不知道NULL类型是啥
if (formatString == null) {
nextDataType = CellDataType.NULL;
formatString = BuiltinFormats.getBuiltinFormat(formatIndex);
}
}
}
解析excel的时候,有两个大问题不好解决:
第一个:空格问题,总是因为各种原因,导致空格检测不出来,造成数据列不对应
第二个:就是字段名称与字段值会对应不上。在我应用中,我是需要将excel解析后,再将数据解析整理成sql语句,并将数据插入到数据库,所以如果数据和字段对应不上,会造成严重的错误。我的解决办法是:使用单元格的索引作为Key
/**
* 解析的excel,配置项 行号:从0开始,列号:从A开始
* 整个报表第一列:数据有效列判断 值:VALID#,说明是要操作插入数据库的列 值为其它:说明无效活或者其他用处
* 第一行:第B列=数据表名称,第C列=删除数据时的筛选条件 第D列=报表标题
* 第二行:从第二列开始:报表参数 key#value
* 第三行:从第二列开始 表字段名,与表中数据列列号对应
*/
static {
/*
条件行
*/
positionMap.put("rowA",new String[]{"0","99"});//标题名,表名,筛选条件所在行号,列无效 第一行
positionMap.put("tablename",new String[]{"0","B"});//表名位置
positionMap.put("condition",new String[]{"0","C"});//筛选你条件位置
positionMap.put("title",new String[]{"0","D"});//标题位置
positionMap.put("rowB",new String[]{"1","99"});//报表参数所在行 第二行
positionMap.put("rowC",new String[]{"2","99"});//表字段所在行 第三行
/*
配置
*/
positionMap.put("validcolumn",new String[]{"99","A"});//有效列所在位置 行无效,A表示第一列 不管行号
}
上面的是配置项,单元格的索引:A1,包括列号和行号,解析每一行数据的时候,只需要列号作为key,所以有:
/*
c:单元格
v:单元格中的元素
*/
//c => 单元格
if ("c".equals(name)) {
//当前单元格位置
String cellIndex = attributes.getValue("r");
//说明是第一列,将行号去掉,只要列号
//if(cellIndex.indexOf("A")!=-1){
cellIndex = cellIndex.replaceAll("\\d+","");
// }
//前一个单元格的位置
if (preRef == null) {
preRef = cellIndex;
} else {
preRef = ref;
}
//当前单元格的位置
ref = cellIndex;
//设定单元格类型
this.setNextDataType(attributes);
}
至此,列号和字段名称以及字段值就相互对应上了。