xls與xlsx的區別:
- .xls是03版Office Microsoft Office Excel 工作表的格式,能被所有的office程序打開
- .xlsx是07版Office Microsoft Office Excel 工作表的格式,只能用2007office以上的版本打開。基于XML的壓縮文件格式取代了其目前專有的默認文件格式,在傳統的文件名擴展名后面添加了字母x(即.docx取代.doc、.xlsx取代.xls,等等),使其占用空間更小。
說明
1.對于 .xls 格式 sheet1 的第1行、第1列命名為 s1_row1_col1_xls
2.代碼中存儲Excel數據的格式為<sheetName,從左到右、從上到下二維數組>的形式
本文使用到的Jar包、Excel文件以及源碼:傳送門
個人博客
一、jxl解析xls格式Excel
由于是直接調用API所以這里直接上代碼
部分代碼:
fun xlsJxl(file: File): Map<String, List<List<String>>> {
val result = ArrayMap<String, List<List<String>>>()
Workbook.getWorkbook(file).sheets.forEach { sheet ->
val rowsList = ArrayList<List<String>>()//列數據
val sheetName = sheet.name
for (i in 0 until sheet.rows) {
rowsList.add(
sheet.getRow(i).map { it.contents }
)
}
result[sheetName] = rowsList
}
return result
}
xls格式Excel文件sheet1內容:
xls格式Excel文件sheet2內容:
xls格式Excel文件sheet3內容:
運行結果:
二、個人理解分析xlsx格式Excel(無法在項目中使用)
由于jxl無法解析xlsx,所以必須另辟蹊徑,在開頭提過,xlsx格式是基于XML文件來生成的,所以我們將.xls
的后綴改為.zip
并將其進行解壓。我們將會看到以下的目錄結構:
xlsx解壓的根目錄:
xlsx解壓根目錄下的xl:
xlsx解壓根目錄下的xl/worksheets:
我們要使用的文件是sharedStrings.xml
文件(里面存在Excel中的數據)、sheet1.xml
、sheet2.xml
、sheet3.xml
。下面通過分析sheet文件與sharedStrings的找到它們的對應關系:
xlsx格式Excel文件sheet1內容:
xlsx格式Excel文件sheet2內容:
xlsx格式Excel文件sheet3內容:
sharedStrings.xml內容:
sheet1.xml內容:
對應關系:
注意在sharedString.xml
上面截圖做記號的紅色框框(從0-35的序號只是為了好理解補充的,實際上里面是不存在的)。從 最后一張圖中可以很好的看出 sheet1.xml
與sharedString.xml
的對應關系,即sheet1中 " v " 標簽里的數字為sharedString中的索引, " row " 標簽為行。所以我們只要解析xml讀取出sharedString中的數據,每個sheet按行讀取索引來整理數據
部分代碼:
fun xlsxSelf(file: File): Map<String, List<List<String>>> {
val result = ArrayMap<String, List<List<String>>>()
//獲取所有元素
val zipFile = ZipFile(file)
val cellsList = ArrayList<String>()
val xmlPullParser = Xml.newPullParser()
zipFile.getInputStream(zipFile.getEntry("xl/sharedStrings.xml"))
.use { inputStream ->
xmlPullParser.setInput(inputStream, "utf-8")
var eventType = xmlPullParser.eventType
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG && xmlPullParser.name == "t") {
cellsList.add(xmlPullParser.nextText())
}
eventType = xmlPullParser.next()
}
}
//獲取cell對應的index,并添加到Map中保存
file.inputStream().use { inputStream ->
ZipInputStream(inputStream).use { zis ->
var zipDir: ZipEntry? = null
while ({ zipDir = zis.nextEntry;zipDir }() != null) {
if (zipDir!!.name.startsWith("xl/worksheets/", true) &&
zipDir!!.name.endsWith("xml", true)
) {
val rowsList = ArrayList<List<String>>()
zipFile.getInputStream(zipFile.getEntry(zipDir!!.name))
.use { inputStream ->
xmlPullParser.setInput(inputStream, "utf-8")
val itemList: ArrayList<String> = ArrayList()//用于保存每行的數據
var eventType = xmlPullParser.eventType
while (eventType != XmlPullParser.END_DOCUMENT) {
when (eventType) {
XmlPullParser.START_TAG -> {
if (xmlPullParser.name == "row") {
itemList.clear()//每行數據開始清空容器
} else if (xmlPullParser.name == "v") {
val index = xmlPullParser.nextText()
itemList.add(cellsList[index.toInt()])
}
}
XmlPullParser.END_TAG -> {
if (xmlPullParser.name == "row") {
//將行數據保存
rowsList.add(itemList.toList())
}
}
}
eventType = xmlPullParser.next()
}
//
result[zipDir!!.name.sheetName()] = rowsList
}
}
}
}
}
return result
}
運行結果:
問題:這只實現了簡單解析 .xlsx 格式的 Excel。其中還存在一些問題未解決,無法獲得到重命名的sheet的名字,即,解壓得到sheet都是按sheet1,sheet2,sheet3命名的。我分析解壓的xml后,還無法找到sheet名字的對應關系。還有一些關于格式不同也會導致解析問題,總的來說還需要對文件進行進一步的分析。所以上面的方法無法使用在正式項目中。
三、POI解析xls/xlsx格式Excel(可應用于項目中)
The Apache POI distribution consists of support for many document file formats. This support is provided in several Jar files. Not all of the Jars are needed for every format. The following tables show the relationships between POI components, Maven repository tags, and the project's Jar files.
Apache POI發行版支持多種文檔文件格式。這種支持是在幾個Jar文件中提供的。并不是每種格式都需要所有的jar。下表顯示了POI組件、Maven存儲庫標記和項目Jar文件之間的關系。
- HSSF是我們將Microsoft Excel 97(-2003)文件格式(BIFF8)移植到純Java的端口 -> xls格式
- XSSF是Microsoft Excel XML(2007+)文件格式(OOXML)到純Java的移植 -> xlsx格式
從上面表格中可以看出我們要解析Excel的xls、xlsx需要導入poi
與poi-ooxml
的jar包,但是由于Android直接引用POI的jar在解析xlsx格式Excel時會出現錯誤,這里我使用之前在Github找到的經過修改后的jar包(但是過挺久的了所以找不到原項目了。詳情請查看源碼)
部分代碼:
fun excelPOI(file: File): Map<String, List<List<String>>> {
val result = ArrayMap<String, List<List<String>>>()
WorkbookFactory.create(file).use { workBook ->
for (sheetIndex in 0 until workBook.numberOfSheets) {
val rowsList = ArrayList<List<String>>()
val sheet = workBook.getSheetAt(sheetIndex)
sheet.rowIterator()
.forEach { row ->
val itemList: ArrayList<String> = ArrayList()//用于保存每行的數據
row.cellIterator().forEach { cell ->
itemList.add(cell.stringCellValue)
}
rowsList.add(itemList)
}
result[sheet.sheetName] = rowsList
}
}
return result
}
POI不僅可以操作Excel,也可以操作其他Office文檔,而且功能特別強大。本文只展示了POI解析Excel中內容的功能,更多功能請查看官方文檔。