xls与xlsx的区别做一下介绍:
-
.xls是03版Office Microsoft Office Excel 工作表的格式,能被所有的office程序打开
-
.xlsx是07版Office Microsoft Office Excel 工作表的格式,只能用2007office以上的版本打开。基于XML的压缩文件格式取代了其目前专有的默认文件格式,在传统的文件名扩展名后面添加了字母x(即.docx取代.doc、.xlsx取代.xls,等等),使其占用空间更小。
说明
- 对于 .xls 格式 sheet1 的第1行、第1列命名为 s1_row1_col_1_xls
解析xls格式Excel
- 解析xls文件使用的是jxl.jar包的方案进行解决。
代码:
public Map<String, List<List<String>>> analyzeXls(String fileName) {
Map<String, List<List<String>>> map = new HashMap<>();
List<List<String>> rows;
List<String> columns = null;
try {
Workbook workbook = Workbook.getWorkbook(new File(fileName));
Sheet[] sheets = workbook.getSheets();
for (Sheet sheet : sheets) {
rows = new ArrayList<>();
String sheetName = sheet.getName();
for (int i = 0; i < sheet.getRows(); i++) {
Cell[] sheetRow = sheet.getRow(i);
int columnNum = sheet.getColumns();
for (int j = 0; j < sheetRow.length; j++) {
if (j % columnNum == 0) { //按行存数据
columns = new ArrayList<>();
}
columns.add(sheetRow[j].getContents());
}
rows.add(columns);
}
map.put(sheetName, rows);
}
//Iterator<Map.Entry<String, List<List<String>>>> iterator = map.entrySet().iterator();
//while (iterator.hasNext()) {
//Map.Entry<String, List<List<String>>> next = iterator.next();
//Iterator<List<String>> iterator1 = next.getValue().iterator();
//while (iterator1.hasNext()) {
//Log.i("zzz", "analyzeXls: sheet --> " + next.getKey() + " row --> " + iterator1.next());
//}
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
从代码可以看出直接使用 jxl 提供的方法得到Workbook就能得到所有的Sheets。并且每个sheet都能得出列数columnNum,所有行的一维数组。这个解析应该不难,重点是xlsx格式的解析。
截图:
.xls 格式 Excel 表格 sheet1 截图
sheet1_xls.png
.xls 格式 Excel 表格 sheet2 截图
sheet2_xls.png
xls 格式 Excel 表格解析截图
xls_result.png
解析xlsx格式Excel
由于xlsx无法使用使用jxl来解析,所以必须另辟蹊径。在开头提过,xlsx格式是基于XML文件来生成的,所以我们将.xlsx的后缀改为.zip并将其进行解压。我们将会其中看到以下的目录结构(根目录和解析要使用到的两个目录):
.xlsx 格式 Excel 解压根目录
xlsx_unzip_root.png
.xlsx 格式 Excel 解压 xl 目录
xlsx_unzip_xl.png
.xlsx 格式 Excel 解压 xl/worksheets 目录
xlsx_unzip_worksheets.png
我们要使用的是 sharedStrings.xml 文件(里面存在Excel中的所有的记录),sheet1.xml,sheet2.xml,sheet3.xml。主要使用的就是这四个文件,通过解析sheet文件找到与sharedStrings的对应关系。具体关系如下截图:
.xlsx 格式 Excel 解压 xl 目录 sharedStrings.xml
sharedString.png
.xlsx 格式 Excel 解压 xl/worksheets 目录 sheet1.xml
sheet1.png sheet1_xlsx.png sheet2_xlsx.png sheet3_xlsx.png sharedString_sheet1.png
注意在上面截图做记号的红色框框(从0-31的序号只是为了好理解补充的,实际上里面是不存在的)。从 最后一张图中可以很好的看出 sheet1.xml与sharedString.xml的关系,即sheet1中 " v " 标签里的数字为sharedString中的索引, " row " 标签为行。所以我们只要解析xml读取出sharedString中的数据,sheet中按行读取中索引。
代码:
private static final String SHAREDSTRINGS = "xl/sharedStrings.xml";
private static final String DIRSHEET = "xl/worksheets/";
private static final String ENDXML = ".xml";
public Map<String, List<List<String>>> analyzeXlsx(String fileName) {
Map<String, List<List<String>>> map = new HashMap<>();
InputStream isShareStrings = null;
InputStream isXlsx = null;
ZipInputStream zipInputStream = null;
listCells = new ArrayList<>();
try {
ZipFile zipFile = new ZipFile(new File(fileName));
ZipEntry sharedStringXML = zipFile.getEntry(SHAREDSTRINGS);//准备xl/sharedStrings.xml文件
isShareStrings = zipFile.getInputStream(sharedStringXML);
//
XmlPullParser xmlPullParser = Xml.newPullParser();//开始解析xl/sharedStrings.xml文件
xmlPullParser.setInput(isShareStrings, "utf-8");
int eventType = xmlPullParser.getEventType();
while (eventType != xmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
String tag = xmlPullParser.getName();
if ("t".equals(tag)) { //如果为 " t " 标签的话将标签中得到元素添加到list集合中
listCells.add(xmlPullParser.nextText());
}
break;
default:
break;
}
eventType = xmlPullParser.next();
}
Log.i("zzz", "analyze: list --> " + listCells);
//
isXlsx = new BufferedInputStream(new FileInputStream(fileName)); //准备遍历xl/worksheets目录下的sheet.xml文件
zipInputStream = new ZipInputStream(isXlsx);
ZipEntry zipDir;
while ((zipDir = zipInputStream.getNextEntry()) != null) {
String dirName = zipDir.getName();
if (!zipDir.isDirectory() && dirName.endsWith(ENDXML)) { // 不是文件夹,且以 ".xml"结尾
if (dirName.contains(DIRSHEET)) { //文件名包含 "xl/worksheets/",则为sheet1.xml与sheet2.xml等
parseSheet(zipFile, dirName, map); //开始解析sheet.xml
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
zipInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
isXlsx.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
isShareStrings.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//Iterator<Map.Entry<String, List<List<String>>>> iterator = map.entrySet().iterator();
//while (iterator.hasNext()) {
//Map.Entry<String, List<List<String>>> next = iterator.next();
//Iterator<List<String>> iterator1 = next.getValue().iterator();
//while (iterator1.hasNext()) {
//Log.i("zzz", "analyzeXls: sheet --> " + next.getKey() + " row --> " + iterator1.next());
//}
//}
return map;
}
private void parseSheet(ZipFile zipFile, String entryName, Map<String, List<List<String>>> map) {
int lastIndexOf = entryName.lastIndexOf(File.separator);
String sheetName = entryName.substring(lastIndexOf + 1, entryName.length() - 4);//得出map的key值,如: sheet1,sheet2等
//
String v = null; //用于存放" v " 标签的值
List<String> colums = null; //用于存放每行的列信息
List<List<String>> rows = new ArrayList<>(); //用于存放每个sheet的行信息
InputStream inputStreamSheet = null;
try {
ZipEntry sheet = zipFile.getEntry(entryName);
inputStreamSheet = zipFile.getInputStream(sheet);
XmlPullParser xmlPullParserSheet = Xml.newPullParser();//开始解析
xmlPullParserSheet.setInput(inputStreamSheet, "utf-8");
int evenTypeSheet = xmlPullParserSheet.getEventType();
while (xmlPullParserSheet.END_DOCUMENT != evenTypeSheet) {
switch (evenTypeSheet) {
case XmlPullParser.START_TAG:
String tag = xmlPullParserSheet.getName();
if ("row".equalsIgnoreCase(tag)) { //如果是每行的开始标签,则初始化列list
colums = new ArrayList<>();
} else if ("v".equalsIgnoreCase(tag)) { //如果是" v "标签则利用得到的索引,得到对应行对应列的元素
v = xmlPullParserSheet.nextText();
if (v != null) {
colums.add(listCells.get(Integer.parseInt(v)));
} else {
colums.add(v);
}
}
break;
case XmlPullParser.END_TAG:
if ("row".equalsIgnoreCase(xmlPullParserSheet.getName()) && v != null) {//一行结束将结构保存在rows中
rows.add(colums);
}
break;
}
evenTypeSheet = xmlPullParserSheet.next();
}
if (rows != null && rows.size() > 0) { //sheet中内容不为空则保存到map中
map.put(sheetName, rows);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
inputStreamSheet.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
截图:
.xlsx 格式 Excel 表格解析截图
xlsx_result.png
jar包和代码:
链接:http://pan.baidu.com/s/1dEQLlVN 密码:blv3
问题:这只实现了简单解析 .xlsx 格式的 Excel。其中还存在一些问题未解决。 无法获得到重命名的sheet的名字,即,解压得到sheet都是按sheet1,sheet2,sheet3命名的。我分析解压的xml后,还无法找到sheet名字的对应关系。
网友评论