美文网首页功能专区Android开发程序员
Android解析Excel(包含.xls/.xlsx)

Android解析Excel(包含.xls/.xlsx)

作者: 请叫我张懂 | 来源:发表于2017-09-26 16:44 被阅读219次

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名字的对应关系。

相关文章

网友评论

    本文标题:Android解析Excel(包含.xls/.xlsx)

    本文链接:https://www.haomeiwen.com/subject/khisextx.html