最近由于工作需要,需要写一个工具,实现搜索功能,数据来源为excel表格。
目前主要实现方式为两种,一种是基于jxl组件,另一种是POI。两种方式的区别在于,jxl 只能读取2003版的excel,即后缀为xls的文件。当今常用的excel都是07版了,使用的为xlsx后缀文件。基于XML的压缩文件格式取代了其目前专有的默认文件格式 xls. 所以如果还用jxl,解析代码中存在 解压缩以及解析xml 的代码。下面会大概提一下这种方法,网上也有很多例子。 JXL已经不再维护更新了,POI 比较强大,可以支持07版本。缺点就是不太适合android,需要重新打包poi,同时还会出现android 65K limit 的问题。网上解决65k 方法很多,这里就不多讲了,此处近贴出读取的部分代码,写excel也很简单,就不贴了。

使用JXL读取excel

读取xls

上面代码基本仅可以读取xls,对于数据的处理可根据自己需求修改。

读取xlsx

读取xlsx,就需要对其进行解压,遍历XML文件来获取所需数据。

public static String readXLSX(String path) { 
 String str = ""; 
 String v = null; 
 boolean flat = false; 
 List<String> ls = new ArrayList<String>(); 
 try { 
 ZipFile xlsxFile = new ZipFile(new File(path)); 
 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(); 
 Log.e("=====>","==xmlParser====>"+xmlParser.toString()); 
 while (evtType != XmlPullParser.END_DOCUMENT) { 
 switch (evtType) { 
 case XmlPullParser.START_TAG: 
 String tag = xmlParser.getName(); 
 if (tag.equalsIgnoreCase("t")) { 
 ls.add(xmlParser.nextText()); 
 Log.e("=====>","===xmlParser===>"+ls.toString()); 
 } 
 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(); 
 while (evtTypesheet != XmlPullParser.END_DOCUMENT) { 
 switch (evtTypesheet) { 
 case XmlPullParser.START_TAG: 
 String tag = xmlParsersheet.getName(); 
 Log.e("=====>","===tag222===>"+tag); 
 if (tag.equalsIgnoreCase("row")) { 
 } else if (tag.equalsIgnoreCase("c")) { 
 String t = xmlParsersheet.getAttributeValue(null, "t"); 
 if (t != null) { 
 flat = true; 
 System.out.println(flat + "有"); 
 } else { 
 System.out.println(flat + "没有"); 
 flat = false; 
 } 
 } else if (tag.equalsIgnoreCase("v")) { 
 v = xmlParsersheet.nextText(); 
 if (v != null) { 
 if (flat) { 
 //new Bean(ls.get(Integer.parseInt(v))) 
 str += ls.get(Integer.parseInt(v)) + " "; 
 } else { 
 str += v + " "; 
 } 
 } 
 } 
 break; 
 case XmlPullParser.END_TAG: 
 if (xmlParsersheet.getName().equalsIgnoreCase("row") 
 && v != null) { 
 str += "\n"; 
 } 
 break; 
 } 
 evtTypesheet = xmlParsersheet.next(); 
 } 
 System.out.println(str); 
 } catch (ZipException e) { 
 e.printStackTrace(); 
 } catch (IOException e) { 
 e.printStackTrace(); 
 } catch (XmlPullParserException e) { 
 e.printStackTrace(); 
 } 
 if (str == null) { 
 str = "解析文件出现问题"; 
 } 
 return str; 
 }

此处代码可以看出 主要是解压遍历

“xl/sharedStrings.xml” 可以按照行取出所有数据(每遍历一次 取出一个单元格内容数据,横向推)
“xl/worksheets/sheet1.xml” 取出行、列的位置,并用换行 空格来替换。 lz 试过可以正常读取数据,但是对数据再进行二次处理感觉比较繁琐,就没有使用这种方法。

使用POI 读取execl

首先尝试直接使用maven库来导入POI包

compile group: 'org.apache.poi', name: 'poi', version: '3.15' 
 compile group: 'org.apache.poi', name: 'poi-ooxml-schemas', version: '3.9' 
 compile group: 'org.apache.poi', name: 'poi-ooxml', version: '3.15'

编译冲突,主要原因是


所以android工程无法直接引用这些包,于是所有的线索指向了 andruhon ,andruhon 重新打包POI ,精简掉一些与excel无用的内容,解决了duplicate class 的问题。具体相关例子可参看https://github.com/andruhon/android5xlsx。

实现代码如下:


public void readExcelByPoi(String filepath) { 
 try { 
 String cellInfo = "1"; 
 InputStream stream =null; 
 stream = new FileInputStream(filepath);//"mnt/sdcard/test.xls" // InputStream stream = getResources().openRawResource(R.raw.test1); 
 XSSFWorkbook workbook = null; 
 try { 
 workbook = new XSSFWorkbook(stream); 
 } catch (IOException e) { 
 e.printStackTrace(); 
 } 
 XSSFSheet sheet = workbook.getSheetAt(0); 
 int rowsCount = sheet.getPhysicalNumberOfRows();// 获取行数 
 // Row row = sheet.getRow(0);// 获取第一行(约定第一行是标题行) 
 // String[] titles = new String[rowsCount]; 
 // for (int i = 0; i < titles.length; i++) { 
 // titles[i] = getCellFormatValue(row.getCell(i));//获取第一行内容 
 // } 
 FormulaEvaluator formulaEvaluator = workbook.getCreationHelper().createFormulaEvaluator(); 
 getWritableDatabase().beginTransaction();// 手动开启事务提高效率 
 for (int r = 1; r < rowsCount; r++) { 
 Log.e("=======>","=======>"+rowsCount); 
 Row row = sheet.getRow(r); 
 int cellsCount = row.getPhysicalNumberOfCells(); 
 nameKey = getCellAsString(row,0, formulaEvaluator); 
 type = getCellAsString(row,1, formulaEvaluator); 
 tab = getCellAsString(row,2, formulaEvaluator); 
 mode = getCellAsString(row,3, formulaEvaluator); 
 initial = getCellAsString(row,8, formulaEvaluator); 
 steps = getCellAsString(row,9, formulaEvaluator); 
 insert(nameKey, type, tab, mode, initial, steps); 
 // for (int c = 0; c < cellsCount; c++) { 
 // String value = getCellAsString(row, c, formulaEvaluator); 
 // cellInfo = "r:" + r + "; c:" + c + "; v:" + value; 
 // Log.e("====>", "====>" + cellInfo); 
 printlnToUser(cellInfo); 
 // } 
 } 
 getWritableDatabase().setTransactionSuccessful(); 
 getWritableDatabase().endTransaction(); 
 } catch (FileNotFoundException e) { 
 e.printStackTrace(); 
 }finally { 
 getWritableDatabase().close(); 
 } 
 }
protected String getCellAsString(Row row, int c, FormulaEvaluator formulaEvaluator) {
    String value = "";
    try {
        org.apache.poi.ss.usermodel.Cell cell = row.getCell(c);
        CellValue cellValue = formulaEvaluator.evaluate(cell);
        switch (cellValue.getCellType()) {
            case org.apache.poi.ss.usermodel.Cell.CELL_TYPE_BOOLEAN:
                value = ""+cellValue.getBooleanValue();
                break;
            case org.apache.poi.ss.usermodel.Cell.CELL_TYPE_NUMERIC:
                double numericValue = cellValue.getNumberValue();
                if(HSSFDateUtil.isCellDateFormatted(cell)) {
                    double date = cellValue.getNumberValue();
                    SimpleDateFormat formatter =
                            new SimpleDateFormat("dd/MM/yy");
                    value = formatter.format(HSSFDateUtil.getJavaDate(date));
                } else {
                    value = ""+numericValue;
                }
                break;
            case org.apache.poi.ss.usermodel.Cell.CELL_TYPE_STRING:
                value = ""+cellValue.getStringValue();
                break;
            default:
        }
    } catch (NullPointerException e) {
        /* proper error handling should be here */
    }
    return value;
}`

此处根据自己项目业务 将数据插入了sqlite 中。
关于POI 如果读取excel 可以参考以下文章:

关于两个jar 包, 由于方法超过65k 需要进行MultiDexApplication 配置,我的项目没有Application,所以只需要在清单文件加上android:name="android.support.multidex.MultiDexApplication"
依赖compile 'com.android.support:multidex:1.0.1' 即可。

精简版的poi 包连接:

目前遗留问题:
Android 开发接触没几年,水平还很低,上述代码存在两个问题 希望大神可以相互交流,给出指正。
1.workbook = new XSSFWorkbook(stream); 处理数据很慢 我的表格的内容大概三千多行,需要五秒。
2. sqlite 插入数据 insert 这个用的感觉很蠢, 不会批量插入,现在就是一条一条插最后提交一个事务,速度也还行。 数据流的关闭上述代码也没处理
基本所有的解决方案都再上面了,希望有更好思路的同学,留言反馈下自己的情况,大家一起学习交流。