最近由于在测试app性能问题,偶然发现服务器存放了两个版本的code码表,分别是xls和xlsx版本的,android端这边是解析的xls,ios那边则是解析的xlsx,为了统一,我查了很多资料然后解析xlsx;查了一下资料,这两者的区别,xls是office excel 03版本的,xlsx是office excel 07版本的。
网上有许多解析xlsx的开源库都可以借鉴的,大多分为三种解析方式,
一种是jxl,这种只能解析xls格式的,更新比较慢,但是库很小,因为是针对android使用的;
另一种是 poi,这种解析很方便,03版和07版excel都可以解析,但是库比较大,超过10M,poi最原始的想法不是给android调用的,里面包含了很多的解析,都是xlsx不需要的;
还有一种解析方式是,把xlsx格式的文件解压出来,然后去到解压目录xl文件夹里面,解析sharedStrings.xml文件即可,但是这种方式的缺点是只能解析很规范的数据,然后纯数字文本是解析不到的(也不知道为什么,亲测是拿不到纯数字文本的,有可能是因为纯数字是居中的,至于原因后续发现了更新博客);
我在项目里用的是第三种方式,也感谢网上的大哥提供的工具类啊,以此鸣谢;
import android.util.Xml;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
public class XLSXParse {
private String _armStr;
private OutFileType _outFileType;
private String _spiltStr;
private ArmFileType _armFileType;
public XLSXParse() {
}
public XLSXParse(Builder builder) {
this._armStr = builder._armStr;
this._outFileType = builder._outFileType;
this._spiltStr = builder._spiltStr;
this._armFileType =builder._armFileType;
}
// 定义最后输出的数据类型
public enum OutFileType{
FILE_TYPE_JSON,//json格式输出
FILE_TYPE_SPILT,//分隔符字符串输出
FILE_TYPE_LIST,//直接输出List类型数据
FILE_TYPE_ARRAY//输出字符串型二位数组
}
public enum ArmFileType{
XLS,
XLSX
}
public static class Builder {
private String _armStr = null;
private OutFileType _outFileType;
private String _spiltStr;
private ArmFileType _armFileType;
public Builder() {
this._spiltStr="|";
this._outFileType= OutFileType.FILE_TYPE_LIST;
}
public Builder(String armFilePath,String split,OutFileType fileType) {
this._armStr=armFilePath;
this._spiltStr=split;
this._outFileType=fileType;
}
//设置以什么格式输出
public Builder setOutFileType(OutFileType outFileType){
this._outFileType=outFileType;
return this;
}
// 设置要解析的XLSX文件路径,其中带文件名
public Builder setArmFilePath(String armFilePath) {
this._armStr = armFilePath;
return this;
}
public Builder setSplitString(String splitString){
this._spiltStr=splitString;
return this;
}
public Builder setArmFileType(ArmFileType armFileType){
this._armFileType = armFileType;
return this;
}
public XLSXParse build() {
return new XLSXParse(this);
}
}
//
private void judgeArmFileType(){
String type=this._armStr.substring(this._armStr.lastIndexOf(".")+1);
if(type!=null){
if(type.equals("xlsx")){
this._armFileType = ArmFileType.XLSX;
}else if(type.equals("xls")){
this._armFileType = ArmFileType.XLS;
}
}
}
public Object parseFile(){
judgeArmFileType();
Object arm=null;
switch (this._armFileType){
case XLSX:
arm = parseXLSX();
break;
case XLS:
arm = parseXLS();
break;
}
return arm;
}
/**
* 开始处理xlsx,根据设置返回相应的数据
* 1⃣️ JSON格式的字符串
* 2⃣️ List数据
* 3⃣️ 用指定字符隔开的字符串
* @return
*/
private Object parseXLSX(){
List<Map<String,String>> list = readXLSX();
Object armObj=null;
if(list.size()>0){
switch (this._outFileType){
case FILE_TYPE_JSON:
armObj = new JSONArray(list);
break;
case FILE_TYPE_LIST:
armObj = list;
break;
case FILE_TYPE_SPILT:
StringBuilder sb=new StringBuilder();
for (int i = 0; i <list.size() ; i++) {
Map<String,String> map =list.get(i);
for (Map.Entry entry : map.entrySet()) {
Object key = entry.getKey();
sb.append(key+":"+map.get(key)+this._spiltStr);
}
}
sb.deleteCharAt(sb.toString().trim().length() - 1);
armObj=sb.toString();
break;
case FILE_TYPE_ARRAY:
break;
}
}
return armObj;
}
// 读取文件内容并且解析
private List<Map<String,String>> readXLSX() {
List<Map<String,String>> armList=new ArrayList<>();
String str = "";
String v = null;
boolean flat = false;
List<String> ls = new ArrayList<String>();
try {
File file =new File(this._armStr);
ZipFile xlsxFile = new ZipFile(file);
ZipEntry sharedStringXML = xlsxFile
.getEntry("xl/sharedStrings.xml");
InputStream inputStream = xlsxFile.getInputStream(sharedStringXML);
XmlPullParser xmlParser = Xml.newPullParser();
xmlParser.setInput(inputStream, "utf-8");
int evtType = xmlParser.getEventType();
while (evtType != XmlPullParser.END_DOCUMENT) {
switch (evtType) {
case XmlPullParser.START_TAG:
String tag = xmlParser.getName();
if (tag.equalsIgnoreCase("t")) {
ls.add(xmlParser.nextText());
}
break;
case XmlPullParser.END_TAG:
break;
default:
break;
}
evtType = xmlParser.next();
}
ZipEntry sheetXML = xlsxFile.getEntry("xl/worksheets/sheet1.xml");
InputStream inputStreamsheet = xlsxFile.getInputStream(sheetXML);
XmlPullParser xmlParsersheet = Xml.newPullParser();
xmlParsersheet.setInput(inputStreamsheet, "utf-8");
int evtTypesheet = xmlParsersheet.getEventType();
String r="";
while (evtTypesheet != XmlPullParser.END_DOCUMENT) {
switch (evtTypesheet) {
case XmlPullParser.START_TAG:
// 获取文件中的各个节点
String tag = xmlParsersheet.getName();
/**
* 判断各个节点的值属于哪一类
*/
if (tag.equalsIgnoreCase("row")) {// 如果xlsx读取到的节点值为row
} else if (tag.equalsIgnoreCase("c")) {// 如果xlsx读取到的节点值为c
String t = xmlParsersheet.getAttributeValue(null, "t");
r= null;
r = xmlParsersheet.getAttributeValue(null, "r");
if (t != null) {
flat = true;
} else {
flat = false;
}
} else if (tag.equalsIgnoreCase("v")) {
v = xmlParsersheet.nextText();
if (v != null) {
Map<String, String> map = new HashMap<>();
if (flat) {
str = ls.get(Integer.parseInt(v)) + "";
}else{
str = v+"";
}
map.put(r, str);
armList.add(map);
}
}
break;
case XmlPullParser.END_TAG:
break;
}
evtTypesheet = xmlParsersheet.next();
}
} catch (ZipException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
}
return armList;
}
/**
* 开始处理xls,根据设置返回相应的数据
* 1⃣️ JSON格式的字符串
* 2⃣️ List数据
* 3⃣️ 用指定字符隔开的字符串
* 4⃣️ 二维数组
* @return
*/
private Object parseXLS(){
String[][] data = readXLS();
Object armObj=null;
if(data.length>0){
switch (this._outFileType){
case FILE_TYPE_JSON:
JSONArray arm =new JSONArray();
for (int i = 0; i <data.length ; i++) {
JSONObject jsonArr=new JSONObject();
for(int j=0;j<data[i].length;j++){
try {
jsonArr.put(i+"_"+j,data[i][j]);
} catch (JSONException e) {
e.printStackTrace();
}
}
arm.put(jsonArr);
}
armObj = arm;
break;
case FILE_TYPE_LIST:
ArrayList<Map<String,String>> list =new ArrayList<>();
for (int i = 0; i <data.length ; i++) {
Map<String,String> map=new HashMap<>();
for (int j=0;j<data[i].length;j++){
map.put(i+"_"+j,data[i][j]);
list.add(map);
}
}
armObj = list;
break;
case FILE_TYPE_SPILT:
StringBuilder sb=new StringBuilder();
for (int i = 0; i <data.length ; i++) {
for (int j=0;j<data[i].length;j++){
sb.append(i+"_"+j+":"+data[i][j]+this._spiltStr);
}
}
sb.deleteCharAt(sb.toString().trim().length() - 1);
armObj=sb.toString();
break;
case FILE_TYPE_ARRAY:
armObj =data;
break;
}
}
return armObj;
}
//读取xls文件内容
public String[][] readXLS() {
String[][]data=null;
try {
Workbook workbook = null;
try {
File file=new File(this._armStr);
workbook = Workbook.getWorkbook(file);
} catch (Exception e) {
throw new Exception("File not found");
}
//得到第一张表
Sheet sheet = workbook.getSheet(0);
//列数
int columnCount = sheet.getColumns();
//行数
int rowCount = sheet.getRows();
if(columnCount>0&&rowCount>0){
data=new String[rowCount][columnCount];
//单元格
Cell cell = null;
for (int everyRow = 0; everyRow < rowCount; everyRow++) {
for (int everyColumn = 0; everyColumn < columnCount; everyColumn++) {
cell = sheet.getCell(everyColumn, everyRow);
data[everyRow][everyColumn]=cell.getContents().trim();
}
}
}
//关闭workbook,防止内存泄露
workbook.close();
} catch (Exception e) {
}
return data;
}
}
使用方式:
XLSXParse.Builder builder = new XLSXParse.Builder()
.setArmFilePath(filePath)
.setOutFileType(XLSXParse.OutFileType.FILE_TYPE_LIST);
XLSXParse xl = builder.build();
List<HashMap<String, String>> list = (List<HashMap<String, String>>) xl.parseFile();