利用POI读取excel文档,并进行信息提取

最近带领小伙伴们处理了一下爬虫爬下来的数据,爬下来的数据有excel表格,word文档,pdf.其中以pdf的数据最难处理,只想要保留pdf中的表格部分,但是却不得其法,目前还没有攻克.

在这篇文章里记录一下使用apache poi读取excel文件用到的方法以及问题等,希望能帮助到大家吧:

一、excel中内容介绍

在excel文件中,我们首先遇到的是一个excel中有多个表,如何遍历和提取每一个单元格的数据这是个问题.

Idea中的pom依赖:

        
            org.apache.poi
            poi-scratchpad
            3.14
        
        
            org.apache.poi
            poi-ooxml
            3.14
        

读取excel文件:

/**
     * 读取Office 2007 excel
     * */
    private Map>> read2007Excel(File file)
            throws IOException {
        // 构造 XSSFWorkbook 对象,strPath 传入文件路径
        XSSFWorkbook xwb = new XSSFWorkbook(new FileInputStream(file));
        evaluator = xwb.getCreationHelper().createFormulaEvaluator();
        //创建一个存储数据的HashMap对象
        Map>> map = new HashMap<>(16);

        // 读取第一章表格内容
        for (int a = 0; a < xwb.getNumberOfSheets(); a++) {
            List> list = new LinkedList<>();

            XSSFSheet sheet = xwb.getSheetAt(a);
            //待存取的value值
            Object value;
            //当前行
            XSSFRow row;
            //当前格
            XSSFCell cell;
            int counter = 0;
            //从第一行开始遍历
            for (int i = sheet.getFirstRowNum();
                 counter < sheet.getPhysicalNumberOfRows(); i++) {
                if (i == 0) {
                    //跳过第一行,不存取标题信息
                    continue;
                }
                row = sheet.getRow(i);
                if (row == null) {
                    break;
                }
                //按行存储,将该表中所有的元素存储在list对象中
                List linked = new LinkedList<>();
                //遍历当前行的所有cell元素
                for (int j = row.getFirstCellNum(); j < row.getLastCellNum(); j++) {
                    cell = row.getCell(j);
                    //判断当前cell存储的数据类型,将其转化为Java中对应的数据类型
                    //例如时间转为Date类型
                    value = getSheetValue(cell);
                    linked.add(value);
                }
                list.add(linked);
            }
            //key为该表的名称,eg:1-潜在,基本信息
            //value为当前表,是一个二维List对象
            map.put(xwb.getSheetName(a),list);
        }
        return map;
    } 
  

看起来还是挺简单的,但是在这一段代码中我用到了evaluator,这个evaluator其实是poi提供的用于处理excel文件中的函数的问题.比如说"='10-评委'!F3"和"SUM(B5:B10)",这样的.通过evaluator就可以达到提取时,不管函数是什么样子,直接获取该函数的结果.我这里把它作为当前类文件的一个属性来使用了.

看完代码,再说一说我读excel文件的思路:为了能够快速的通过表签名(也就是最下面那一栏)找到该表,我创建了一个Map>>对象,其中key就是表签的名字.遮掩我通过map的get方法就能够快速的获取到这个表格.

表格中我按行读取,所以value值为一个二维的List集合,遍历当前表的所有元素只需要两个for循环嵌套就可以了.

二、通过Object存数据,方便以后入库

我存储成Object的唯一目的就是为了方便以后存储到数据库中,避免了来回转换格式的问题.可是同样的,我在对单个object进行判断和取值的时候,就需要疯狂的转来转去.如果有需要,大家可以清一色的存储成String类型.

/**
     * @Description : 获取cell对象中的值,并且转为java中对应的类型
     *
     * @param cell
     * @return : java.lang.Object
     * @author : 申劭明
     * @date : 2019/8/9 15:50
     */
    public Object getSheetValue(Cell cell){
        Object value ;
        if (cell == null) {
            //导入不能为空
            value = "无";
            return value;
        }
        // 格式化 number String
        DecimalFormat df = new DecimalFormat("0");
        // 字符,格式化日期字符串
        SimpleDateFormat sdf = new SimpleDateFormat(
                "yyyy-MM-dd");
        // 格式化数字
        DecimalFormat nf = new DecimalFormat("0");
        //判断当前单元格的元素类型
        switch (cell.getCellType()) {
            case XSSFCell.CELL_TYPE_STRING:
                value = cell.getStringCellValue();
                break;
            case XSSFCell.CELL_TYPE_NUMERIC:
                value = cell.getNumericCellValue();
                //避免手机号,身份证等自动识别为double类型
                //由于精度问题需要用减法判断大小
                if (80000000.0 - (double)value < 0){
                    value = (long)((double)value);
                    value = String.valueOf(value);
                }
                break;
            case XSSFCell.CELL_TYPE_BOOLEAN:
                value = cell.getBooleanCellValue();
                break;
            //空格,空白
            case XSSFCell.CELL_TYPE_BLANK:
                value = "";
                break;
            case XSSFCell.CELL_TYPE_FORMULA:
                value = "";
                try{
                    //可能会有Invalid sheetIndex异常
                    value = getCellValue(evaluator.evaluate(cell));
                }catch (Exception e){
                    e.printStackTrace();
                }

                break;
            default:
                value = cell.toString();
        }
        if (value == null || "".equals(value)) {
            //导入不能为空
            value = "无";
        }
        return value;
    }

三、源代码,共计两个文件

Table.java:主要负责区分excel文件版本和提取值的工作

package com.mbyte.easy.util;


import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.xssf.usermodel.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * @Author: 申劭明
 * @Date: 2019/8/9 17:26
 */
public class Table {

    /**
     * 一个excel文件读取后的结果存放在map集合中
     */
    private Map>> map;

    private static XSSFFormulaEvaluator evaluator;

    public Table(){

    }

    public Map>> getMap() {
        return map;
    }

    /**
     * @Description : 读取一个excel文件,根据它的文件类型选择对应的读取方法
     *
     * @param filePath
     * @author : 申劭明
     * @date : 2019/8/10 14:33
    */
    public Table(String filePath) throws IOException {
        this.map = readExcel(new File(filePath));
    }

    /**
     * @Description : 读取Excel文件,在该方法中通过后缀名判断要调用哪一个方法
     *
     * @param file 待读取的excel文件
     * @return : Map<表签的名,List>
     * @author : 申劭明
     * @date : 2019/8/9 15:10
     */
    public Map>> readExcel(File file) throws IOException {
        String fileName = file.getName();
        String extension = fileName.lastIndexOf(".") == -1 ? "" : fileName
                .substring(fileName.lastIndexOf(".") + 1);
        if ("xls".equals(extension)) {
            return read2003Excel(file);
        } else if ("xlsx".equals(extension)) {
            return read2007Excel(file);
        } else {
            throw new IOException("不支持的文件类型");
        }
    }

    /**
     * 读取 office 2003 excel
     *
     * @throws IOException
     */
    private Map>> read2003Excel(File file)
            throws IOException {
        Map>> map = new HashMap<>(16);
        //读取.xls文件
        HSSFWorkbook hwb = new HSSFWorkbook(new FileInputStream(file));

        //遍历标签:基本信息,1-潜在,3-文件递交表...
        for (int a = 0; a < hwb.getNumberOfSheets();a++) {
            List> list = new LinkedList<>();

            HSSFSheet sheet;
            //获取表格对象
            sheet = hwb.getSheetAt(a);
            //输出对象
            Object value = null;
            HSSFRow row = null;
            HSSFCell cell = null;
            int counter = 0;
            for (int i = sheet.getFirstRowNum(); counter < sheet
                    .getPhysicalNumberOfRows(); i++) {
                row = sheet.getRow(i);
                //一行
                List linked = new LinkedList<>();
                for (int j = row.getFirstCellNum(); j <= row.getLastCellNum(); j++) {
                    cell = row.getCell(j);

                    //获取value值
                    value = getSheetValue(cell);

                    linked.add(value);

                }
                list.add(linked);
            }
            //key为该表的名称,eg:1-潜在,基本信息
            //value为当前表,是一个二维List对象
            map.put(hwb.getSheetName(a),list);
        }
        return map;
    }
    /**
     * 读取Office 2007 excel
     * */
    private Map>> read2007Excel(File file)
            throws IOException {
        // 构造 XSSFWorkbook 对象,strPath 传入文件路径
        XSSFWorkbook xwb = new XSSFWorkbook(new FileInputStream(file));
        evaluator = xwb.getCreationHelper().createFormulaEvaluator();
        //创建一个存储数据的HashMap对象
        Map>> map = new HashMap<>(16);

        // 读取第一章表格内容
        for (int a = 0; a < xwb.getNumberOfSheets(); a++) {
            List> list = new LinkedList<>();

            XSSFSheet sheet = xwb.getSheetAt(a);
            //待存取的value值
            Object value;
            //当前行
            XSSFRow row;
            //当前格
            XSSFCell cell;
            int counter = 0;
            //从第一行开始遍历
            for (int i = sheet.getFirstRowNum();
                 counter < sheet.getPhysicalNumberOfRows(); i++) {
                if (i == 0) {
                    //跳过第一行,不存取标题信息
                    continue;
                }
                row = sheet.getRow(i);
                if (row == null) {
                    break;
                }
                //按行存储,将该表中所有的元素存储在list对象中
                List linked = new LinkedList<>();
                //遍历当前行的所有cell元素
                for (int j = row.getFirstCellNum(); j < row.getLastCellNum(); j++) {
                    cell = row.getCell(j);
                    //判断当前cell存储的数据类型,将其转化为Java中对应的数据类型
                    //例如时间转为Date类型
                    value = getSheetValue(cell);
                    linked.add(value);
                }
                list.add(linked);
            }
            //key为该表的名称,eg:1-潜在,基本信息
            //value为当前表,是一个二维List对象
            map.put(xwb.getSheetName(a),list);
        }
        return map;
    }

    /**
     * @Description : 获取cell对象中的值,并且转为java中对应的类型
     *
     * @param cell
     * @return : java.lang.Object
     * @author : 申劭明
     * @date : 2019/8/9 15:50
     */
    public Object getSheetValue(Cell cell){
        Object value ;
        if (cell == null) {
            //导入不能为空
            value = "无";
            return value;
        }
        // 格式化 number String
        DecimalFormat df = new DecimalFormat("0");
        // 字符,格式化日期字符串
        SimpleDateFormat sdf = new SimpleDateFormat(
                "yyyy-MM-dd");
        // 格式化数字
        DecimalFormat nf = new DecimalFormat("0");
        //判断当前单元格的元素类型
        switch (cell.getCellType()) {
            case XSSFCell.CELL_TYPE_STRING:
                value = cell.getStringCellValue();
                break;
            case XSSFCell.CELL_TYPE_NUMERIC:
                value = cell.getNumericCellValue();
                //避免手机号,身份证等自动识别为double类型
                //由于精度问题需要用减法判断大小
                if (80000000.0 - (double)value < 0){
                    value = (long)((double)value);
                    value = String.valueOf(value);
                }
                break;
            case XSSFCell.CELL_TYPE_BOOLEAN:
                value = cell.getBooleanCellValue();
                break;
            //空格,空白
            case XSSFCell.CELL_TYPE_BLANK:
                value = "";
                break;
            case XSSFCell.CELL_TYPE_FORMULA:
                value = "";
                try{
                    //可能会有Invalid sheetIndex异常
                    value = getCellValue(evaluator.evaluate(cell));
                }catch (Exception e){
                    e.printStackTrace();
                }

                break;
            default:
                value = cell.toString();
        }
        if (value == null || "".equals(value)) {
            //导入不能为空
            value = "无";
        }
        return value;
    }

    /**
     * @Description : 将公式得到的cellValue转为String对象
     *
     * @param cell
     * @return : java.lang.String
     * @author : 申劭明
     * @date : 2019/8/12 14:25
    */
    private String getCellValue(CellValue cell) {
        String cellValue = null;
        switch (cell.getCellType()) {
            case Cell.CELL_TYPE_STRING:
                cellValue=cell.getStringValue();
                break;

            case Cell.CELL_TYPE_NUMERIC:
                cellValue=String.valueOf(cell.getNumberValue());
                break;
            case Cell.CELL_TYPE_FORMULA:
                break;
            default:
                break;
        }

        return cellValue;
    }
}
 
  

 

ReadExcel.java : 主要用于调用和筛选我想要入库的数据,我用的是SSM框架,为了省去自己写连接数据库等操作,我在这提供了接口,大概不是很容易明白我的思路,所以能不看就别看了- -.

package com.mbyte.easy.util;

import com.mbyte.easy.entities.Specialist;
import com.mbyte.easy.entities.Totals;
import com.mbyte.easy.expert.entity.ExpertInfo;
import com.mbyte.easy.score.entity.BidderScore;

import java.io.IOException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Author: 申劭明
 * @Date: 2019/8/9 14:53
 */
public class ReadExcel {

    /**
     * 声明一个table对象,也就是任意个对象都共享同一个excel文件
     * 不支持线程操作
     */
    private Table table;

    /**
     * 专家列表
     */
    private List speciaList;

    /**
     * 得分列表
     */
    private List totalsList;

    public String getFilePath() {
        return filePath;
    }

    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }

    /**
     * 读取的文件地址
     */
    private String filePath;

    public static void main(String[] args) {
        try {
            ReadExcel readExcel = new ReadExcel("D:\\test.xlsx");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 读取Excel文件
     * 如果文件类型不是excel类型的文件,将抛出一个异常
     *
     * @param filePath excel文件路径
     * @throws Exception
     * @author 申劭明
     */
    public ReadExcel(String filePath) throws Exception {

        boolean have = filePath.contains(".xlsx") || filePath.contains(".xls");
        if (!have){
            throw new Exception("文件类型不符合要求");
        }
        this.filePath = filePath;

        //专家(评委会)表
        List> experts = null;
        //汇总表
        List> totals = null;
        try {
            table = new Table(filePath);
            Map>> map = table.getMap();

            for (String sheetName : map.keySet()) {
                //评委会中有专家信息,汇总中有公司名称等信息
                boolean flag =  sheetName.contains("评委会") || sheetName.contains("汇总") ;

                if (flag){
                    //当前表格的所有数据
                    for (List objects : map.get(sheetName)) {
                        if (sheetName.contains("评委会")){
                            experts = map.get(sheetName);
                            experts = experts.subList(1,experts.size());
                            continue;
                        }else if(sheetName.contains("汇总")){
                            totals = map.get(sheetName);
                            continue;
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //从专家List中提取需要存储到数据库中的信息
        if (experts != null) {
            speciaList = getExport(experts);
        }

        /*从汇总表中提取出要存储到数据库中的信息
         *因为汇总表中有公司信息,所以如果公司名称为空,那就没必要获取汇总了
         */
        if(totals != null){
            totalsList = getTotals(totals,speciaList);
        }
    }
    
    /**
     * @Description : 获得专家信息表
     * 
     * @return : void
     * @author : 申劭明
     * @date : 2019/8/12 17:28
    */
    public List getExperts(){
        List list = new LinkedList<>();

        for (Specialist expert : speciaList) {
            ExpertInfo expertInfo = new ExpertInfo(expert);
            list.add(expertInfo);
        }
        return list;
    }

    /**
     * @Description : 获得成绩表
     *
     * @return : java.util.List
     * @author : 申劭明
     * @date : 2019/8/12 17:48
    */
    public List getScores(){
        return totalsList;
    }

    /**
     * @Description : 获取打分情况
     * 由于打分需要评委姓名,分数以及公司名称,所以需要调用之前的两个list
     * 
     * @param totals
     * @return : java.util.List
     * @author : 申劭明
     * @date : 2019/8/10 9:20
    */
    private List getTotals(List> totals, List specialists) {
        List list = new LinkedList<>();

        //总计有多少个公司参与
        int countOfCompanys = -1;

        //公司名称所在的那一行
        List companys = new ArrayList<>(10);

        //第一个成绩,总计取 countOfCompanys 个数据
        int firstScoreIndex = -1;

        //评分人姓名索引
        int nameCount = -1;
        //给list数组添加元素
        for (List total : totals) {
            if (nameCount > specialists.size()){
                break;
            }
            //获取所有参与评分的公司,由于存在合并单元格的问题,导致读取会出现异常
            for (Object object : total) {
                //判断该object是否为String类型
                if (object instanceof String){
                    String target = (String)object;
                    //项目名称中可能含有'公司'字段,所以用委员会成员+1作为判断节点
                    if (target.contains("投标人名称") && countOfCompanys < 0){
                        firstScoreIndex = total.indexOf(object) + 1;
                        //公司数量
                        countOfCompanys = total.size() - firstScoreIndex ;
                        companys = total.subList(firstScoreIndex,firstScoreIndex + countOfCompanys);
                        break;
                    }
                }
            }
            if (firstScoreIndex >0){
                for (int i = 0; i < countOfCompanys ; i++) {
                    BidderScore tempTotal = new BidderScore();
                    try {
                        if (total.get(i + firstScoreIndex) instanceof Double ) {
                            double score = (Double) total.get(i + firstScoreIndex);
                            tempTotal.setFirmName((String)companys.get(i));
                            DecimalFormat df = new DecimalFormat("#.00");
                            String str = df.format(score);
                            tempTotal.setScore(str);
                            tempTotal.setExpertName(specialists.get(nameCount).getName());
                            list.add(tempTotal);
                        }else if(total.get(i + firstScoreIndex) instanceof String ){
                            //在Table中将所有公式得到的double类型都转为了String类型
                            if (((String)total.get(i + firstScoreIndex)).contains(".")){
                                double score = Double.valueOf(((String)total.get(i + firstScoreIndex)).trim());
                                DecimalFormat df = new DecimalFormat("#.00");
                                String str = df.format(score);
                                tempTotal.setScore(str);
                                tempTotal.setFirmName((String)companys.get(i));
                                tempTotal.setExpertName(specialists.get(nameCount).getName());
                                list.add(tempTotal);
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                nameCount++;
            }
            if (companys.size() > 0 && companys.get(0).equals("0.0")){
                //满足equals说明数据存在问题,去其他的表寻找公司名称
                Map>> map = table.getMap();
                for (String s : map.keySet()) {
                    if (s.contains("评委") && s.contains("评分")){
                        List> tempList = map.get(s);
                        int temp = -1;
                        for (List objects : tempList) {
                            for (Object o : objects) {
                                if (o instanceof String){
                                    if (((String) o).contains("分值")){
                                        temp = objects.indexOf(o) + 1;
                                        companys = objects.subList(temp,temp + countOfCompanys);
                                        break;
                                    }
                                }
                            }
                            if (temp > 0){
                                break;
                            }
                        }
                    }
                }
                //将获得的list数组中的公司设置为新找到的公司名称
                for (int i = 0; i < list.size(); i++) {
                    list.get(i).setFirmName((String)companys.get(i));
                }
            }
        }
        return list;
    }

    /**
     * @Description : 获取所有的专家信息
     *
     * @return : com.mbyte.easy.entities.Specialist
     * @author : 申劭明
     * @date : 2019/8/9 17:39
    */
    public List getExport(List> experts){

        List list = new ArrayList<>();

        //指示姓名列的位置
        int nameIndex = -1;
        //指示工作单位列
        int workIndex = -1;
        //指示联系电话列
        int phoneIndex = -1;
        //指示出生年月列
        int birthIndex = -1;
        //指示身份证号
        int idIndex = -1;

        for (List expert : experts) {
            for (Object object : expert) {
                if (object instanceof String){
                    String target = (String)object;
                    if (target.contains("姓名")){
                        nameIndex = expert.indexOf(object);
                    }else if (target.contains("工作单位")){
                        workIndex = expert.indexOf(object);
                    }else if (target.contains("联系电话")){
                        phoneIndex = expert.indexOf(object);
                    }else if (target.contains("身份证号")){
                        idIndex = expert.indexOf(object);
                    }
                }
            }
                try{
                    if (nameIndex >= 0){
                        Specialist specialist = new Specialist();
                        specialist.setName((String)expert.get(nameIndex));
                        if (workIndex >= 0){
                            specialist.setWorkPlace((String)expert.get(workIndex));
                        }
                        if (phoneIndex > 0){
                            specialist.setPhone((String)expert.get(phoneIndex));
                        }
                        if (idIndex > 0){
                            if (expert.get(idIndex) instanceof String){
                                specialist.setBirthDay((String)expert.get(idIndex));
                            }
                        }
                        list.add(specialist);
                    }
                }catch (Exception e){
                    System.err.println("第" + experts.indexOf(expert) + "个数据异常");
                    e.printStackTrace();
                }
            }
            //第一行为姓名,工作单位,身份证号,联系电话
            list.remove(0);
            return list;
    }

    /**
     * @Description : 身份证号转Date
     *
     * @param id
     * @return : java.util.Date
     * @author : 申劭明
     * @date : 2019/8/9 20:59
    */
    public Date id2Date(String id) throws ParseException {

        id = id.substring(6,14);
        SimpleDateFormat dateFormat = new SimpleDateFormat(DateUtil.PATTERN_yyyyMMdd);
        Date date = dateFormat.parse(id);
        return date;
    }

}
 
  

 

你可能感兴趣的:(Java,Excel读取,POI,Java,爬虫,数据提取)