Java 讀寫 excel 實戰完全解析


本文微信公眾號「AndroidTraveler」首發。

背景

時值畢業季,很多畢業生初入職場。

因此,這邊也寫了一些新手相關的 Android 技術點。

比如上一篇的 Android 開發你需要了解的那些事 就是列舉了一些小點,避免新手 Android 開發者踩坑。

同時,也是恰逢暑假,因此大學生處於放假階段。

這一篇主要是來自一位大學生的提問。

因此這邊分享一下我個人的解題思路和方法,希望能夠對他有所啟發。

歡迎大家交流分享。

題目

使用語言:JAVA

需求:讀取一個Excel表格里面的數據(例如:姓名+分數),對其進行重新排序(按分數高低),然后輸出在另一個Excel表格。

分析

一般對需求我們都采取拆分思維。
將大問題拆成小問題,小問題解決了,整個大問題也就解決了。

這個需求很明確,需要解決三個問題:

  1. 讀取 Excel 表格數據
  2. 對數據排序
  3. 將數據寫入另一個 Excel 表格

我們這里要求使用 Java 語言,而 Java 語言一個很重要的點就是面向對象。

因此首先我們要考慮一下,這個題目里面有哪些類需要我們創建。

大概可以想象需要下面這些類:

讀取數據類:ExcelReader
寫入數據類:ExcelWriter
數據排序類:由於 Java API 自帶,所以不需要重復造輪子
數據模型類:StudentScore
啟動類:ParserStart,帶有 main 方法

大概的 UML 圖如下:

此時我們可以寫出 v0.1 代碼:
ExcelReader.java:

import java.util.List;
public class ExcelReader {
	public List<StudentScore> read(String fileName) {
		//TODO
		return null;
	}
}

ExcelWriter.java:

import java.util.List;
public class ExcelWriter {
	public void write(String fileName, List<StudentScore> list) {
		//TODO
	}
}

StudentScore.java:

public class StudentScore {
	private String name;
	private int score;
	
	public StudentScore(String name, int score) {
		super();
		this.name = name;
		this.score = score;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getScore() {
		return score;
	}
	public void setScore(int score) {
		this.score = score;
	}
}

ParserStart.java:

import java.util.List;

public class ParserStart {
	public static void main(String[] args) {
		// 第一步:讀取數據
		List<StudentScore> dataList = new ExcelReader().read("input.xls");
		// 第二步:排序
		//TODO
		// 第三部:寫入數據
		new ExcelWriter().write("output.xls", dataList);
	}
}

好了,基本框架搭好了。接下來就一步一步來實現我們的方法。

v0.2 代碼:完善 ExcelReader 的 read 方法

Excel 的讀取方法有第三方的庫可以使用,因此我們不需要自己寫。
我們這里使用的是第三方的 Apache 提供的 POI 庫。
下載鏈接地址:https://poi.apache.org/download.html
寫這篇文章時使用到的版本是 4.1.0
解壓然后將 jar 包引入 Eclipse 項目即可。

接下來就是實際編寫代碼了,詳情見注釋。

我們要讀取的文件示例如下:

ExcelReader.java:

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

public class ExcelReader {
	public List<StudentScore> read(String fileName) throws EncryptedDocumentException, IOException {
		if (fileName == null) return null;
		
		File xlsFile = new File(fileName);
		if (!xlsFile.exists()) return null;
		
		// 工作表
		Workbook workbook = WorkbookFactory.create(xlsFile);
		// 表個數
		int numberOfSheets = workbook.getNumberOfSheets();
//		System.out.println(numberOfSheets);
		if (numberOfSheets <= 0) return null;
		
		List<StudentScore> list = new ArrayList<>();
		//我們的需求只需要處理一個表,因此不需要遍歷
		Sheet sheet = workbook.getSheetAt(0);
		// 行數
		int rowNumbers = sheet.getLastRowNum() + 1;
//		System.out.println(rowNumbers);
		StudentScore score;
		// 讀數據,第二行開始讀取
		for (int row = 1; row < rowNumbers; row++) {
			Row r = sheet.getRow(row);
//			System.out.println(r.getPhysicalNumberOfCells());
			//我們只需要前兩列
			if (r.getPhysicalNumberOfCells() >= 2) {
				score = new StudentScore(r.getCell(0).toString(), (int) Double.parseDouble(r.getCell(1).toString()));
				list.add(score);
			} 
		}
		return list;
	}
}

v0.3 代碼:對讀取后的數據做排序處理

在 v0.2 版本中,我們成功讀取了數據,但是我們讀取的數據是按照 Excel 里面的順序的,因此我們需要做排序處理。Java 函數庫有對集合進行排序的方法。不過我們需要對 Model 進行額外處理,添加排序規則。因為排序可以是從小到大排,也可以是從大到小排。

StudentScore.java:

public class StudentScore implements Comparable<StudentScore>{
	
	private String name;
	private int score;
	
	public StudentScore(String name, int score) {
		super();
		this.name = name;
		this.score = score;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getScore() {
		return score;
	}
	public void setScore(int score) {
		this.score = score;
	}
	
	@Override
	public String toString() {
		return "StudentScore [name=" + name + ", score=" + score + "]";
	}
	
	@Override
	public int compareTo(StudentScore o) {
		return o.score - this.score;
	}
	
}

ParserStart.java:

import java.util.Collections;
import java.util.List;

public class ParserStart {

	public static void main(String[] args) throws Exception{
		// 第一步:讀取數據
		List<StudentScore> dataList = new ExcelReader().read("resource/input.xls");
		System.out.println(dataList);
		// 第二步:排序
		Collections.sort(dataList);
		System.out.println(dataList);
		// 第三部:寫入數據
//		new ExcelWriter().write("output.xls", dataList);
	}

}

v0.4 代碼:將排序后的數據寫入另一個 excel 表中

在 v0.3 版本中,我們完成了數據的排序,接下來我們需要將排好序的數據寫到 output.xls 中。

ExcelWriter.java

import java.io.File;
import java.io.IOException;
import java.util.List;

import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;

public class ExcelWriter {

	public void write(String fileName, List<StudentScore> list)  {
		HSSFWorkbook workbook = new HSSFWorkbook();

		HSSFSheet sheet = workbook.createSheet("StudentScore");

		// 創建Excel標題行,第一行
		HSSFRow headRow = sheet.createRow(0);
		headRow.createCell(0).setCellValue("姓名");
		headRow.createCell(1).setCellValue("分數");

		// 往Excel表中遍歷寫入數據
		for (StudentScore studentScore : list) {
			createCell(studentScore, sheet);
		}

		File xlsFile = new File(fileName);
		try {
			// 或者以流的形式寫入文件 workbook.write(new FileOutputStream(xlsFile));
			workbook.write(xlsFile);
		} catch (IOException e) {
		    // TODO
		} finally {
			try {
				workbook.close();
			} catch (IOException e) {
			    // TODO
			}	
		}
	}

	// 創建Excel的一行數據。
	private void createCell(StudentScore studentScore, HSSFSheet sheet) {
		HSSFRow dataRow = sheet.createRow(sheet.getLastRowNum() + 1);
		dataRow.createCell(0).setCellValue(studentScore.getName());
		dataRow.createCell(1).setCellValue(studentScore.getScore());
	}

}

ParserStart.java

import java.util.Collections;
import java.util.List;

public class ParserStart {

	public static void main(String[] args) throws Exception {
		// 第一步:讀取數據
		List<StudentScore> dataList = new ExcelReader().read("resource/input.xls");
		System.out.println(dataList);
		// 第二步:排序
		Collections.sort(dataList);
		System.out.println(dataList);
		// 第三部:寫入數據
		new ExcelWriter().write("resource/output.xls", dataList);
	}

}

到此,通過幾個版本的迭代,我們的需求就實現了。

NOTE:
在本項目中,input.xls 放在 resource 文件夾下面。所以最終版本傳入的路徑是 resource/input.xls。另外輸出的時候這邊發現 Eclipse 沒有顯示出來 output.xls,需要刷新一下。
此外,下載我的項目運行驗證時,可能需要修改下 JRE。
另外 jar 包不要引入錯位置了:

當然,還有幾個待完善的點需要說明下:

  1. 這里沒有對輸入表的數據做合法性校驗,比如分數為負數的情況是否需要做一些提示之類的操作。
  2. 這里判斷文件不存在時,直接返回 null。而且沒有判斷文件是否為 excel 文件。這里就交由大家完善。而且這邊異常沒有做處理,直接 throws。
  3. 這里因為簡單就沒有做抽象。但是考慮可能需要讀寫 word 或者 pdf 或者其他文件,所以可以考慮引入繼承和多態。抽取基類。
  4. 合理組織文件夾和命名。

另外說一下有什么應用場景吧,其實還真有。

移動端有多語言,想象一下產品給你一張帶有多語言的 excel 表。
如果你一個一個拷貝到多個語言的資源文件下,這效率難以想象。
而如果你用了這一節的內容,分分鍾讀取 excel 按照你要的規則組裝后輸出到控制台。
想想就有點 6 啊。

好了,本期內容到此結束,歡迎留言交流討論。

如果你有想了解的知識點,歡迎公眾號留言私信,也許下一個 pick 的就是你。

源碼獲取地址:
https://github.com/nesger/JavaSamples/tree/master/ParseExcel

參考鏈接:
Java讀取Excel數據:基於Apache POI(一)
Java讀取和解析Excel數據:基於Apache POI(二)
Java導出數據行寫入到Excel表格:基於Apache POI


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM