Android解析Excel(xls/xlsx)


xls與xlsx的區(qū)別:

  • .xls是03版Office Microsoft Office Excel 工作表的格式,能被所有的office程序打開
  • .xlsx是07版Office Microsoft Office Excel 工作表的格式,只能用2007office以上的版本打開?;赬ML的壓縮文件格式取代了其目前專有的默認(rèn)文件格式,在傳統(tǒng)的文件名擴(kuò)展名后面添加了字母x(即.docx取代.doc、.xlsx取代.xls,等等),使其占用空間更小。

說明

1.對于 .xls 格式 sheet1 的第1行、第1列命名為 s1_row1_col1_xls

2.代碼中存儲Excel數(shù)據(jù)的格式為<sheetName,從左到右、從上到下二維數(shù)組>的形式

本文使用到的Jar包、Excel文件以及源碼:傳送門
個人博客

一、jxl解析xls格式Excel

由于是直接調(diào)用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>>()//列數(shù)據(jù)
        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內(nèi)容:


xls格式Excel文件sheet1內(nèi)容

xls格式Excel文件sheet2內(nèi)容:


xls格式Excel文件sheet2內(nèi)容

xls格式Excel文件sheet3內(nèi)容:


xls格式Excel文件sheet3內(nèi)容

運行結(jié)果:


jxl_xls_result.png

二、個人理解分析xlsx格式Excel(無法在項目中使用)

由于jxl無法解析xlsx,所以必須另辟蹊徑,在開頭提過,xlsx格式是基于XML文件來生成的,所以我們將.xls的后綴改為.zip并將其進(jìn)行解壓。我們將會看到以下的目錄結(jié)構(gòu):

xlsx解壓的根目錄:


xlsx解壓的根目錄

xlsx解壓根目錄下的xl:


xlsx解壓根目錄下的xl

xlsx解壓根目錄下的xl/worksheets:


xlsx解壓根目錄下的xl/worksheets

我們要使用的文件是sharedStrings.xml文件(里面存在Excel中的數(shù)據(jù))、sheet1.xml、sheet2.xmlsheet3.xml。下面通過分析sheet文件與sharedStrings的找到它們的對應(yīng)關(guān)系:

xlsx格式Excel文件sheet1內(nèi)容:


xlsx格式Excel文件sheet1內(nèi)容

xlsx格式Excel文件sheet2內(nèi)容:


xlsx格式Excel文件sheet2內(nèi)容

xlsx格式Excel文件sheet3內(nèi)容:


xlsx格式Excel文件sheet3內(nèi)容

sharedStrings.xml內(nèi)容:


sharedStrings.xml內(nèi)容

sheet1.xml內(nèi)容:


sheet1.xml內(nèi)容

對應(yīng)關(guān)系:


對應(yīng)關(guān)系

注意在sharedString.xml上面截圖做記號的紅色框框(從0-35的序號只是為了好理解補充的,實際上里面是不存在的)。從 最后一張圖中可以很好的看出 sheet1.xmlsharedString.xml的對應(yīng)關(guān)系,即sheet1中 " v " 標(biāo)簽里的數(shù)字為sharedString中的索引, " row " 標(biāo)簽為行。所以我們只要解析xml讀取出sharedString中的數(shù)據(jù),每個sheet按行讀取索引來整理數(shù)據(jù)

部分代碼:

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對應(yīng)的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()//用于保存每行的數(shù)據(jù)
                            var eventType = xmlPullParser.eventType
                            while (eventType != XmlPullParser.END_DOCUMENT) {
                                when (eventType) {
                                    XmlPullParser.START_TAG -> {
                                        if (xmlPullParser.name == "row") {
                                            itemList.clear()//每行數(shù)據(jù)開始清空容器
                                        } else if (xmlPullParser.name == "v") {
                                            val index = xmlPullParser.nextText()
                                            itemList.add(cellsList[index.toInt()])
                                        }
                                    }
                                    XmlPullParser.END_TAG -> {
                                        if (xmlPullParser.name == "row") {
                                            //將行數(shù)據(jù)保存
                                            rowsList.add(itemList.toList())
                                        }
                                    }
                                }
                                eventType = xmlPullParser.next()
                            }
                            //
                            result[zipDir!!.name.sheetName()] = rowsList
                        }
                }
            }
        }
    }
    return result
}

運行結(jié)果:


運行結(jié)果

問題:這只實現(xiàn)了簡單解析 .xlsx 格式的 Excel。其中還存在一些問題未解決,無法獲得到重命名的sheet的名字,即,解壓得到sheet都是按sheet1,sheet2,sheet3命名的。我分析解壓的xml后,還無法找到sheet名字的對應(yīng)關(guān)系。還有一些關(guān)于格式不同也會導(dǎo)致解析問題,總的來說還需要對文件進(jìn)行進(jìn)一步的分析。所以上面的方法無法使用在正式項目中。

三、POI解析xls/xlsx格式Excel(可應(yīng)用于項目中)

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發(fā)行版支持多種文檔文件格式。這種支持是在幾個Jar文件中提供的。并不是每種格式都需要所有的jar。下表顯示了POI組件、Maven存儲庫標(biāo)記和項目Jar文件之間的關(guān)系。

POI_components
  • HSSF是我們將Microsoft Excel 97(-2003)文件格式(BIFF8)移植到純Java的端口 -> xls格式
  • XSSF是Microsoft Excel XML(2007+)文件格式(OOXML)到純Java的移植 -> xlsx格式

從上面表格中可以看出我們要解析Excel的xls、xlsx需要導(dǎo)入poipoi-ooxml的jar包,但是由于Android直接引用POI的jar在解析xlsx格式Excel時會出現(xiàn)錯誤,這里我使用之前在Github找到的經(jīng)過修改后的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()//用于保存每行的數(shù)據(jù)
                        row.cellIterator().forEach { cell ->
                            itemList.add(cell.stringCellValue)
                        }
                        rowsList.add(itemList)
                    }
                result[sheet.sheetName] = rowsList
            }
        }
        return result
    }

POI不僅可以操作Excel,也可以操作其他Office文檔,而且功能特別強大。本文只展示了POI解析Excel中內(nèi)容的功能,更多功能請查看官方文檔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)自鏈接 目錄 1.認(rèn)識NPOI 2.使用NPOI生成xls文件 2.1創(chuàng)建基本內(nèi)容 2.1.1創(chuàng)建Workboo...
    腿毛褲閱讀 11,156評論 1 3
  • 最近由于工作需要,需要寫一個工具,實現(xiàn)搜索功能,數(shù)據(jù)來源為excel表格。目前主要實現(xiàn)方式為兩種,一種是基于jxl...
    依靜軒閱讀 6,662評論 1 9
  • 目錄 1.應(yīng)用場景,資料搜索以及思路確認(rèn) 2.DHxlsReader(解讀XLS) 3.利用GDataXML解讀X...
    碎月Coder閱讀 13,154評論 15 19
  • 該文章為本系列的第二篇第一篇為 : Java POI操作Excel(User Model)第三篇為 : Java ...
    mmlmml閱讀 9,648評論 0 5
  • 歐洲的美食很多,不同國家有不同的代表,下面為大家?guī)砜偨Y(jié)的歐洲美食點評! 英國 炸魚薯條這個算是英國街頭巷尾最常見...
    劃過天堂的風(fēng)閱讀 236評論 0 0

友情鏈接更多精彩內(nèi)容