最近带领小伙伴们处理了一下爬虫爬下来的数据,爬下来的数据有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
看起来还是挺简单的,但是在这一段代码中我用到了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
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;
}
}