Bootstrap

Excel文件解析(Apache POl框架解析)

一、概述

        在应用程序的开发过程中,经常需要使用 Excel文件来进行数据的导入或导出。所以,在通过Java语言实现此类需求的时候,往往会面临着Excel文件的解析(导入)或生成(导出)。
        在Java技术生态圈中,可以进行Excel文件处理的主流技术包括: Apache POI 、JXL、Alibaba EasyExcel等。
        Apache POI基于 DOM方式进行解析,将文件直接加载内存,所以速度较快,适合 Excel文件数据量不︰大的应用场景。JXL只支持Excel 2003以下版本,所以不太常见。
Alibaba EasyExcel采用逐行读取的解析模式,将每一行的解析结果以观察者的模式通知处理(AnalysisEventListener),所以比较适合数据体量较大的Excel文件解析。

超大文件的Excel读写:

使用SXSSFWorkbook进行写入,通过设置SXXFWorkbook的构造参数,可以设置每次在内存中保持的行数,当达到这个值的时候,那么会把这些数据flush到磁盘上,这样就不会出现内存不够的情况。

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.UUID;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

public class Demo01 {
	public static void main(String[] args) {
		try (// 设置内存中最多保留的行数
				SXSSFWorkbook workbook = new SXSSFWorkbook(100);
				OutputStream os = new FileOutputStream("E:\\apesourcefile\\homework\\apachepoibig.xlsx")) {
			Sheet sheet = workbook.createSheet();
			for (int i = 0; i < 100000; i++) {
				Row row = sheet.createRow(i);
				row.createCell(0).setCellValue(UUID.randomUUID().toString().substring(0, 8));
				row.createCell(1).setCellValue(new Date());
			}
			workbook.write(os);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("内容写出结束");
	}
}

二、Apache POl


        Apache POI 是用Java编写的免费开源的跨平台的 Java API , Apache POI提供给Java程序对Microsoft Office格式档案进行读写功能的API开源类库。
它分别提供对不同格式文件的解析:

HSSF-提供读写Microsoft Excel格式档案的功能。
XSSF-提供读写Microsoft Excel OOXML格式档案的功能。
HWPF-提供读写Microsoft Word格式档案的功能。
HSLF-提供读写Microsoft PowerPoint格式档案的功能。
HDGF-提供读写Microsoft Visio格式档案的功能。

三、XSSF解析Excel文件


HSSF 用于解析旧版本(*.xls)Excel文件,由于旧版本的Excel文件只能存在65535行数据,所以目前已经不常用。所以目前主要采用XSSF 进行新版本(*.xlsx) Exce文件的解析。

添加Jar包依赖: 

1.excel文件写出


        workbook接口代表一个Excel 文件,用于创建或加载(解析) Excel文件。常见实现类是XSSFWorkbook 。

指定区域写出 

public class Demo01 {
	public static void main(String[] args) throws IOException {
		// 创建工作薄对象
		XSSFWorkbook workbook = new XSSFWorkbook();

		// 创建一个工作表
		XSSFSheet sheet = workbook.createSheet();

		// 创建一个行对象
		Row row = sheet.createRow(0);

		// 创建单元格对象
		Cell cell1 = row.createCell(0);
		cell1.setCellValue("Python");
		// 创建单元格对象
		Cell cell2 = row.createCell(1);
		cell2.setCellValue("C#");

		Row row1 = sheet.createRow(1);
		row1.createCell(0).setCellValue("Pandas");
		row1.createCell(1).setCellValue("EasyX");

		// 输出操作
		OutputStream os = new FileOutputStream("E:\\apesourcefile\\homework\\excel1.xlsx");
		workbook.write(os);

		// 关闭流
		workbook.close();
		os.close();
		System.out.println("写出结束");
	}
}

一列一列写出 

public class Demo02 {
	public static void main(String[] args) throws IOException {
		String path = "E:\\apesourcefile\\homework\\excel2.xlsx";
		OutputStream os = new FileOutputStream(path);
		// 创建工作薄对象
		XSSFWorkbook workbook = new XSSFWorkbook();

		// 创建一个工作表
		XSSFSheet sheet = workbook.createSheet("检验码表");

		// 创建一个行对象
		Row row1 = sheet.createRow(0);
		row1.createCell(0).setCellValue("序号");
		row1.createCell(1).setCellValue("优惠码");
		row1.createCell(2).setCellValue("校验码");
		row1.createCell(3).setCellValue("创建日期");

		for (int i = 1; i <= 1000; i++) {
			Row row = sheet.createRow(i);
			row.createCell(0).setCellValue(i);
			String uuid = UUID.randomUUID().toString();
			row.createCell(1).setCellValue(uuid.substring(0, 8));
			row.createCell(2).setCellValue(uuid.substring(32));
			LocalDateTime now = LocalDateTime.now();
			DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日  HH:mm:ss");
			row.createCell(3).setCellValue(now.format(formatter));
		}
		// 输出操作
		workbook.write(os);
		// 关闭流
		workbook.close();
		os.close();
		System.out.println("写出结束");
	}
}

设置单元格数据格式

public class Demo03 {
	public static void main(String[] args) {
		String path = "E:\\apesourcefile\\homework\\excel3.xlsx";
		try (OutputStream os = new FileOutputStream(path); XSSFWorkbook workbook = new XSSFWorkbook()) {

			// 设置单元格数据格式
			// 1.DateFormate数据格式化对象
			XSSFDataFormat dataFormat = workbook.createDataFormat();
			// 2.进行不同的格式编号
			Short dateShort = dataFormat.getFormat("yyyy-MM-dd");
			Short moneyShort = dataFormat.getFormat("¥###,#");
			// 3.创建单元格样式对象,并为其赋值
			// 3.1创建日期格式
			CellStyle dateStyle = workbook.createCellStyle();
			dateStyle.setDataFormat(dateShort);
			// 3.2创建金额格式
			CellStyle moneyStyle = workbook.createCellStyle();
			moneyStyle.setDataFormat(moneyShort);
			// 3.3创建加粗居中等单元格样式
			CellStyle headerCellStyle = workbook.createCellStyle();
			// 设置单元格的水平对齐类型,此时水平居中
			headerCellStyle.setAlignment(HorizontalAlignment.CENTER);
			// 设置单元格的垂直对齐类型,此时垂直靠底边
			headerCellStyle.setVerticalAlignment(VerticalAlignment.BOTTOM);
			// 创建并设置字体
			Font font = workbook.createFont();
			font.setBold(true);
			font.setColor(Font.COLOR_RED);
			headerCellStyle.setFont(font);

			XSSFSheet sheet = workbook.createSheet("金额表");
			Row row = sheet.createRow(0);
			row.createCell(0).setCellValue("序号");
			row.createCell(1).setCellValue("金额");
			row.createCell(2).setCellValue("日期");

			// 创建数据
			for (int i = 1; i <= 100; i++) {
				Row row1 = sheet.createRow(i);
				Cell cell0 = row1.createCell(0);
				cell0.setCellStyle(headerCellStyle);
				cell0.setCellValue(i);

				Cell cell1 = row1.createCell(1);
				cell1.setCellStyle(moneyStyle);
				cell1.setCellValue((int) (Math.random() * 100000));

				Cell cell2 = row1.createCell(2);
				cell2.setCellStyle(dateStyle);
				cell2.setCellValue(new Date());

			}
			// 输出操作
			workbook.write(os);
			System.out.println("写出结束");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

2.excel文件写入

指定sheet遍历

//POI进行数据的读入
public class Demo04 {
	public static void main(String[] args) {
		try (// 创建输入流,将输入流和工作薄连接起来
				InputStream is = new FileInputStream("E:\\apesourcefile\\homework\\excel3.xlsx");
				Workbook workbook = new XSSFWorkbook(is)) {
			// 获取sheet表
			Sheet sheet = workbook.getSheet("金额表");
			for (Row row : sheet) {
				for (Cell cell : row) {
					System.out.print(cell + " ");
				}
				System.out.println();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

 一行一行读取

//POI进行数据的读取
public class Demo05 {
	public static void main(String[] args) {
		try (// 创建输入流,将输入流和工作薄连接起来
				InputStream is = new FileInputStream("E:\\apesourcefile\\homework\\excel3.xlsx");
				Workbook workbook = new XSSFWorkbook(is)) {
			// 获取sheet表
			Sheet sheet = workbook.getSheetAt(0);
			for (int i = 0; i <= sheet.getLastRowNum(); i++) {
				// 获取行对象
				Row row = sheet.getRow(i);
				for (int j = 0; j < row.getLastCellNum(); j++) {
					// 通过行对象获取单元格对象
					Cell cell = row.getCell(j);
					// 获取单元格的类型
					CellType cellType = cell.getCellType();
					// 根据单元格的类型获取单元格中的值
					if (CellType.STRING == cellType) {
						System.out.print(cell.getStringCellValue() + " ");
					} else if (CellType.NUMERIC == cellType) {
						System.out.print(cell.getNumericCellValue() + " ");
					}
				}
				System.out.println();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

例子:对上述Excel文件进行检查,找出优惠码和校验码中包含“0”的数据,并保存至一个文本文件。

public class Demo06 {
	public static void main(String[] args) {
		try (// 创建输入流,将输入流和工作薄连接起来
				InputStream is = new FileInputStream("E:\\apesourcefile\\homework\\excel2.xlsx");
				Workbook workbook = new XSSFWorkbook(is);
				OutputStream os = new FileOutputStream("E:\\apesourcefile\\homework\\result.txt")) {
			// 获取sheet表
			Sheet sheet = workbook.getSheetAt(0);
			for (Row row : sheet) {
				for (int i = 1; i <= 2; i++) {
					Cell cell = row.getCell(i);
					if (cell.getCellType() == CellType.STRING) {
						String str = cell.getStringCellValue();
						if (str.contains("0")) {
							os.write((str + System.lineSeparator()).getBytes());
						}
					}
				}
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

例题:我这里有一个userData.xlsx类型的文件,请你读取我这个文件里的:

Data [no=99999, name=察合台, dept=教学保障中心]

data类型的对象:

public class Data {
	private int no;
	private String name;
	private String dept;

	public Data(int no, String name, String dept) {
		super();
		this.no = no;
		this.name = name;
		this.dept = dept;
	}

	public int getNo() {
		return no;
	}

	public void setNo(int no) {
		this.no = no;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDept() {
		return dept;
	}

	public void setDept(String dept) {
		this.dept = dept;
	}

	@Override
	public String toString() {
		return "Data [no=" + no + ", name=" + name + ", dept=" + dept + "]";
	}
	
}

 测试类:

public class Demo07 {
	public static void main(String[] args) {
		String path = "E:\\apesourcefile\\homework\\userData.xlsx";
		List<Data> listDatas = new ArrayList<Data>();
		try (InputStream is = new FileInputStream(path); Workbook workbook = new XSSFWorkbook(is);) {
			// 获取sheet表
			Sheet sheet = workbook.getSheetAt(0);
			// 通过增强for循环获取行和单元格
			for (int i = 0; i <= sheet.getLastRowNum(); i++) {
				Row row = sheet.getRow(i);
				int no = (int) row.getCell(0).getNumericCellValue();
				String dept = row.getCell(1).getStringCellValue();
				String name = row.getCell(2).getStringCellValue();
				Data data = new Data(no, name, dept);
				listDatas.add(data);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		for (Data data : listDatas) {
			System.out.println(data);
		}

	}
}

 输出结果:

四、相关例题

题目

Test01:按照指定逻辑验证指定Excel文件demo-data.xlsx,并按照格式输出验证结果。

 

public class Work {
	public static void main(String[] args) {
		// 1.序号是否连续
		// 2.检查性别是否为男或女
		// 3.身份证号
		// 3.1 身份证号码格式(必须为18位)
		// 3.2 身份证号码不能重复
		// 3.3 身份证号码开头两位是否与籍贯符合
		// 北京 11 天津12 河北 13 山西14 内蒙古 15
		// 陕西61 甘肃62 青海 63
		// 4.学历只能填写:大专、本科、硕士、其它
		// 5.体重在40-120之间

		List<String> errorMsgList = 
				validateDataExcel("c:\\test\\run\\demo-data.xlsx");

		if (errorMsgList.size() == 0) {
			System.out.println("文件检查无误!");
		} else {
			// 显示所有错误信息
			for (String err : errorMsgList) {
				System.out.println(err);
			}
		}

	}

	public static List<String> validateDataExcel(String excelDataFilePath) {
		return null;
	}

}

验证结果:

第3行身份证与籍贯不符!
第4行序号有误!
第7行身份证号码长度有误!
第9行身份证号码长度有误!
第10行身份证与籍贯不符!
第11行身份证号码长度有误!
第12行序号有误!
第13行身份证号码长度有误!
第16行性别有误!
第18行序号有误!
第22行身份证与籍贯不符!
第24行身份证号码(61252319930120222X)重复!
第25行性别有误!
第33行身份证号码(610523199203206313)重复!
第36行身份证号码长度有误!
第39行身份证与籍贯不符!
第41行身份证与籍贯不符!
第43行身份证与籍贯不符!
第44行身份证与籍贯不符!
第46行身份证与籍贯不符!

 解答

方法1: 

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class Work03_result {
	public static void main(String[] args) {
		List<String> errorMsgList = validateDataExcel("E:\\apesourcefile\\homework\\demo-data.xlsx");
		if (errorMsgList.size() == 0) {
			System.out.println("文件检查无误!");
		} else {
			// 显示所有错误信息
			for (String err : errorMsgList) {
				System.out.println(err);
			}
		}

	}

	public static List<String> validateDataExcel(String excelDataFilePath) {
		List<String> resultList = new ArrayList<String>();
		Set<String> identifySet = new HashSet<String>();
		HashMap<String, String> localcationMap = new HashMap<String, String>();
		// 北京 11 天津12 河北 13 山西14 内蒙古 15
//		 陕西61 甘肃62 青海 63
		localcationMap.put("北京", "11");
		localcationMap.put("天津", "12");
		localcationMap.put("河北", "13");
		localcationMap.put("山西", "14");
		localcationMap.put("内蒙古", "15");
		localcationMap.put("陕西", "61");
		localcationMap.put("甘肃", "62");
		localcationMap.put("青海", "63");
		List<String> studyList = new ArrayList<String>(Arrays.asList("大专", "本科", "硕士", "其他"));

		try (InputStream is = new FileInputStream(excelDataFilePath); XSSFWorkbook workbook = new XSSFWorkbook(is)) {
			XSSFSheet sheet = workbook.getSheetAt(0);
			// 读取行
			for (int i = 1; i <= sheet.getLastRowNum(); i++) {
				Row row = sheet.getRow(i);
				// 1.序号是否连续
				int num = (int) row.getCell(0).getNumericCellValue();
				if (i != num) {
					resultList.add("第" + i + "行序号有误!");
				}
				// 2.检查性别是否为男或者女
				String sex = row.getCell(2).getStringCellValue();
				if (!("男".equals(sex) || "女".equals(sex))) {
					resultList.add("第" + i + "行性别有误!");
				}
				// 3.身份证号
				// 3.1 身份证号码格式(必须为18位)
				String identifyCard = row.getCell(3).getStringCellValue();
				if (identifyCard.length() != 18) {
					resultList.add("第" + i + "行身份证号码长度有误!");
				}
				// 3.2 身份证号码不能重复
				if (!identifySet.add(identifyCard)) {
					resultList.add("第" + i + "行身份证号码" + identifyCard + "重复!");
				}
				// 3.3 身份证号码开头两位是否与籍贯符合
				String id = row.getCell(6).getStringCellValue();
				if (!localcationMap.get(id).equals(identifyCard.substring(0, 2))) {
					resultList.add("第" + i + "行身份证与籍贯不符合!");
				}
				// 4.学历只能填写:大专、本科、硕士、其它
				String study = row.getCell(7).getStringCellValue();
				if (!studyList.contains(study)) {
					resultList.add("第" + i + "行学历有误!");
				}
				// 5.体重在40-120之间
				double weight = row.getCell(8).getNumericCellValue();
				if (weight >= 120 || weight <= 40) {
					resultList.add("第" + i + "体重有误!");
				}

			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return resultList;
	}

}

方法2:

import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Work03 {
	public static void main(String[] args) {
		String path = "E:\\apesourcefile\\homework\\demo-data.xlsx";
		List<String> list = DataExcel(path);
		if (list.size() == 0) {
			System.out.println("文件检查无误!");
		} else {
			// 错误信息
			for (String err : list) {
				System.out.println(err);
			}
		}
	}

	public static List<String> DataExcel(String excelDataFilePath) {
		List<String> msg = new ArrayList<>();
		Set<String> identifyNumberSet = new HashSet<>();
		try (FileInputStream fis = new FileInputStream(excelDataFilePath); 
				Workbook workbook = new XSSFWorkbook(fis)) {
			Sheet sheet = workbook.getSheetAt(0);
			int previousSeq = 0;
			for (Row row : sheet) {
				int rowIndex = row.getRowNum();
				if (rowIndex == 0) {
					continue; // 跳过第一行
				}
				// 获取序号格
				Cell seqCell = row.getCell(0);
				// 判断是否为空或者不是String类型的
				if (seqCell == null || seqCell.getCellType() != CellType.NUMERIC) {
					msg.add("序号错误或缺失: 第 " + (rowIndex + 1) + " 行");
					continue;
				}
				// 1.序号是否连续
				int seq = (int) seqCell.getNumericCellValue();
				if (seq != previousSeq + 1) {
					msg.add("序号不连续: 第 " + (rowIndex + 1) + " 行");
				}
				previousSeq = seq;
				// 性别
				Cell genderCell = row.getCell(1);
				// 判断是否为空或者不是String类型的
				if (genderCell == null || genderCell.getCellType() != CellType.STRING) {
					msg.add("性别错误或缺失: 第 " + (rowIndex + 1) + " 行");
					continue;
				}
				// 2.检查性别是否为男或女
				String gender = genderCell.getStringCellValue();
				if (!gender.equals("男") && !gender.equals("女")) {
					msg.add("性别错误: 第 " + (rowIndex + 1) + " 行");
				}

				// 身份证号
				Cell idNumberCell = row.getCell(2);
				// 判断是否为空或者不是String类型的
				if (idNumberCell == null || idNumberCell.getCellType() != CellType.STRING) {
					msg.add("身份证号码错误或缺失: 第 " + (rowIndex + 1) + " 行");
					continue;
				}
				String idNumber = idNumberCell.getStringCellValue();
				// 3.身份证号
				// 3.1 身份证号码格式(必须为18位)
				if (idNumber.length() != 18) {
					msg.add("身份证号码长度错误: 第 " + (rowIndex + 1) + " 行");
				}
				// 3.2 身份证号码不能重复
				if (identifyNumberSet.contains(idNumber)) {
					msg.add("身份证号码重复: 第 " + (rowIndex + 1) + " 行");
				} else {
					identifyNumberSet.add(idNumber);
				}
				// 3.3 身份证号码开头两位是否与籍贯符合
				// 北京 11 天津12 河北 13 山西14 内蒙古 15
				// 陕西61 甘肃62 青海 63
				if (idNumber.length() >= 2) {
					String location = idNumber.substring(0, 2);
					if (!Location(location)) {
						msg.add("身份证号码开头与籍贯不符: 第 " + (rowIndex + 1) + " 行");
					}
				} else {
					msg.add("身份证号码长度不足以验证开头: 第 " + (rowIndex + 1) + " 行");
				}

				// 学历
				Cell educationCell = row.getCell(3);
				// 判断是否为空或者不是String类型的
				if (educationCell == null || educationCell.getCellType() != CellType.STRING) {
					msg.add("学历错误或缺失: 第 " + (rowIndex + 1) + " 行");
					continue;
				}
				// 4.学历只能填写:大专、本科、硕士、其它
				String education = educationCell.getStringCellValue();
				if (!education.equals("大专") && !education.equals("本科") && !education.equals("硕士")
						&& !education.equals("其它")) {
					msg.add("学历填写错误: 第 " + (rowIndex + 1) + " 行");
				}

				// 体重
				Cell weightCell = row.getCell(4);
				if (weightCell == null || weightCell.getCellType() != CellType.NUMERIC) {
					msg.add("体重错误或缺失: 第 " + (rowIndex + 1) + " 行");
					continue;
				}
				// 5.体重在40-120之间
				double weight = weightCell.getNumericCellValue();
				if (weight < 40 || weight > 120) {
					msg.add("体重错误: 第 " + (rowIndex + 1) + " 行");
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
			msg.add("文件读取失败: " + e.getMessage());
		}
		return msg;
	}

	private static boolean Location(String location) {
//		判断地区是否正确
//		 北京 11 天津12 河北 13 山西14 内蒙古 15
//				 陕西61 甘肃62 青海 63
		String[] validPrefixes = { "11", "12", "13", "14", "15", "61", "62", "63" };
		for (String prefix : validPrefixes) {
			if (prefix.equals(location)) {
				return true;
			}
		}
		return false;
	}
}

Test02:请将demo-data.xlsx文件中Sheet1中的数据复制到文件的最后一个Sheet中,并根据身份证号码,补充出生日期和年龄两列里的值。 

 解答:

方法1:

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.LocalDate;
import java.time.DateTimeException;
import java.time.format.DateTimeFormatter;

public class Work04 {
	public static void main(String[] args) {
		String path = "E:\\apesourcefile\\homework\\demo-data.xlsx";
		try (FileInputStream fis = new FileInputStream(path); XSSFWorkbook workbook = new XSSFWorkbook(fis)) {
			// 获取 Sheet1
			Sheet sheet1 = workbook.getSheetAt(0);
			// 创建一个新的 Sheet 作为最后一个 Sheet
			Sheet newSheet = workbook.createSheet("Copied");

			// 复制 Sheet1 中的数据到新创建的 Sheet 中
			copySheetData(sheet1, newSheet);

			// 计算出生日期和年龄并填充数据
			for (int i = 1; i <= newSheet.getLastRowNum(); i++) {
				Row row = newSheet.getRow(i);
				Cell identityCard = row.getCell(3); // 身份证号码

				if (identityCard != null) { // 判断是否为空
					String idCard = identityCard.getStringCellValue();
					if (idCard.length() >= 18) {
						// 提取出生日期字段
						String birthDateStr = idCard.substring(6, 14);
						try {
							DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
							LocalDate birthDate = LocalDate.parse(birthDateStr, formatter);
							// 计算年龄
							int age = calculateAge(birthDate, LocalDate.now());
							// 填充出生日期和年龄
							Cell birthDateCell = row.createCell(4);
							birthDateCell.setCellValue(birthDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")));

							Cell ageCell = row.createCell(5);
							ageCell.setCellValue(age);
						} catch (DateTimeException e) {
							System.err.println("生日参数不合法,无法计算年龄 " + birthDateStr);
						}
					}
				}
			}

			// 写入到文件
			try (FileOutputStream fos = new FileOutputStream(path)) {
				workbook.write(fos);
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private static int calculateAge(LocalDate birthDate, LocalDate currentDate) {
		// 计算年龄方法
		int age = currentDate.getYear() - birthDate.getYear();

		// 检查是否需要减少一年
		if (currentDate.getMonthValue() < birthDate.getMonthValue()
				|| (currentDate.getMonthValue() == birthDate.getMonthValue()
						&& currentDate.getDayOfMonth() < birthDate.getDayOfMonth())) {
			age--;
		}
		return age;
	}

	private static void copySheetData(Sheet sourceSheet, Sheet destinationSheet) {
		for (int i = 0; i <= sourceSheet.getLastRowNum(); i++) {// 遍历原工作表的所有行
			Row sourceRow = sourceSheet.getRow(i);
			Row newRow = destinationSheet.createRow(i);// 创建目标工作表的新行

			if (sourceRow != null) {
				for (int j = 0; j < sourceRow.getLastCellNum(); j++) {// 遍历当前行中的所有单元格
					Cell newCell = newRow.createCell(j);
					Cell sourceCell = sourceRow.getCell(j);

					if (sourceCell != null) {// 复制单元格内容到目标行
						switch (sourceCell.getCellType()) {
						case STRING:
							newCell.setCellValue(sourceCell.getStringCellValue());
							break;
						case NUMERIC:
							newCell.setCellValue(sourceCell.getNumericCellValue());
							break;
						case BOOLEAN:
							newCell.setCellValue(sourceCell.getBooleanCellValue());
							break;
						case FORMULA:
							newCell.setCellFormula(sourceCell.getCellFormula());
							break;
						case BLANK:
							newCell.setBlank();
							break;
						default:
							break;
						}
					}
				}
			}
		}
	}
}

方法2: 

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.time.LocalDate;

public class Work04_result {
	public static void main(String[] args) {
		String excelDataFilePath = "E:\\apesourcefile\\homework\\demo-data.xlsx";
		try (XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(excelDataFilePath));
				OutputStream os = new FileOutputStream(excelDataFilePath)) {
			// 根据旧的表获取数据进行数据处理
			XSSFSheet sheet = workbook.getSheetAt(0);
			// 根据工作薄对象创建一个新的表
			XSSFSheet sheet1 = workbook.createSheet("新表");

			// 复制头信息
			Row r = sheet.getRow(0);// 获取要复制的表的第一个行
			Row row0 = sheet1.createRow(0);// 新表创建头信息行
			// 获取表头单元格的信息并复制到新表的头信息中
			for (int i = 0; i < r.getLastCellNum(); i++) {
				String valueString = r.getCell(i).getStringCellValue();
				row0.createCell(i).setCellValue(valueString);
			}

			// 复制表中数据--并进行数据处理
			for (int i = 1; i <= sheet.getLastRowNum(); i++) {
				Row row = sheet.getRow(i);// 旧数据的行
				Row row1 = sheet1.createRow(i);// 新表行
				String cardId = null;// 每行的身份证需要保留,在进行年龄和出生日期保存时使用
				// 循环获取单元格
				for (int j = 0; j < row.getLastCellNum(); j++) {
					Cell cell = row.getCell(j);
					if (j == 3) {
						cardId = row.getCell(j).getStringCellValue();
					}
					// 如果是下标为4或者5的单元格,通过保存身份证信息处理出生日期和年龄
					if (j == 4) {
						Cell cell1 = row.createCell(4);
						cell1.setCellValue(cardId.substring(6, 14));
					}
					if (j == 5) {
						Cell cell2 = row1.createCell(5);
						int age = LocalDate.now().getYear() - Integer.parseInt(cardId.substring(6, 10));
						cell2.setCellValue(age);
					}
					// 除了45单元格,其他单元格信息直接从旧表复制
					if (cell.getCellType() == CellType.STRING) {
						row1.createCell(j).setCellValue(cell.getStringCellValue());
					} else if (cell.getCellType() == CellType.NUMERIC) {
						row1.createCell(j).setCellValue(cell.getNumericCellValue());
					}
				}
			}
			workbook.write(os);
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("输出结束");
	}
}

;