欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Excel和XML的相互转换(JAVA语言)

程序员文章站 2024-02-23 21:15:16
...

Excel导出

——————————————————————————————————————
部分引用:CSDN博主「zEthan」https://blog.csdn.net/ethan_10/article/details/80335350

一天的成果。。。累死我了(

环境配置

2.1.1 下载jar

官方下载:http://poi.apache.org/download.html
,目前最新版本是3.7,这里使用比较稳定的3.6版。

2.1.2 加入jar包

将根目录下的poi-3.6-20091214.jar和Lib目录下三个通用包 commons-logging-1.1.jar junit-3.8.1.jar log4j-1.2.13.jar拷贝到项目的Lib下

2.2 Jakarta POI HSSF API组件

HSSF(用于操作Excel的组件)提供给用户使用的对象在rg.apache.poi.hssf.usermodel包中,主要部分包括Excel对象,样式和格式,还有辅助操作。有以下几种对象:
常用组件:

HSSFWorkbook excel的文档对象

HSSFSheet excel的表单

HSSFRow excel的行

HSSFCell excel的格子单元

HSSFFont excel字体

HSSFDataFormat 日期格式

HSSFHeader sheet头

HSSFFooter sheet尾(只有打印的时候才能看到效果)

样式:

HSSFCellStyle cell样式

辅助操作包括:

HSSFDateUtil 日期

HSSFPrintSetup 打印

HSSFErrorConstants 错误信息表

2.3 基本操作步骤

首先,理解一下一个Excel的文件的组织形式,一个Excel文件对应于一个workbook(HSSFWorkbook),一个workbook可以有多个sheet(HSSFSheet)组成,一个sheet是由多个row(HSSFRow)组成,一个row是由多个cell(HSSFCell)组成。

基本操作步骤:

1、用HSSFWorkbook打开或者创建“Excel文件对象”

2、用HSSFWorkbook对象返回或者创建Sheet对象

3、用Sheet对象返回行对象,用行对象得到Cell对象

4、对Cell对象读写。

下面来看一个动态生成Excel文件的例子:

package TEST;

import static org.apache.poi.hssf.usermodel.HeaderFooter.fontSize;

import java.io.FileOutputStream;
import java.util.Calendar;
import java.util.Date;

import org.apache.poi.hssf.usermodel.HSSFFooter;
import org.apache.poi.hssf.usermodel.HSSFHeader;
import org.apache.poi.hssf.usermodel.HSSFPrintSetup;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;

public class test {
	
	public static void main(String[] args) throws Exception {
		//创建HSSFWorkbook对象
		Workbook wb = new HSSFWorkbook();
		//创建HSSFSheet对象
		Sheet sheet = wb.createSheet("sheet0");
		//创建HSSFRow对象
		Row row = sheet.createRow(0);
		//创建HSSFCell对象
		Cell cell=row.createCell(0);
		//设置单元格的值
		cell.setCellValue("chinese in sheet");
		//输出Excel文件
		//FileOutputStream output=new FileOutputStream("d:\\workbook.xls");
		FileOutputStream fileOutStream = new FileOutputStream("D:\\workbook.xls"); 
		wb.write(fileOutStream);
		fileOutStream.flush();
		}
	}

HSSF读取文件同样还是使用这几个对象,只是把相应的createXXX方法变成了getXXX方法即可。可见只要理解了其中原理,不管是读还是写亦或是特定格式都可以轻松实现,正所谓知其然更要知其所以然。

2.4 关于flush()方法

简单来说,由于CPU不能够直接访问外存(硬盘),而需要借助于内存来完成对硬盘中数据的读/取操作。

并且,JAVA中的输入/输出流在默认情况下,是不被缓存区缓存的,因此,每发生一次read()方法和write()方法都需要请求操作系统再分发/接收一个字节,这样程序的运行效率必然会降低,所以,我们将硬盘中的数据事先添加到预定义范围(大小合适)的缓冲池中来预存数据,待CPU产生I/O操作时,可以从这个缓存池中来读取数据,这样便减少了CPU的I/O的次数,提高了程序的运行效率。

read()方法和write()是线程阻塞的,也就是说,当某个线程试图向另一端网络节点读取或写入数据时,有可能会发生网络连接异常或者是服务器短期内没有响应,这将会导致该线程阻塞,同样地,在无数据状态进行读取,数据已满进行写操作时,同样会发生阻塞,这时,其他线程抢占资源后继续执行。如果出现此现状,读取到缓冲池中的数据不能够及时的发送到另一端的网络节点,需要该线程再次竞争到CPU资源才可正常发送。

还有一种情况,当我们将数据预存到缓冲池中时,当数据的长度满足缓冲池中的大小后,才会将缓冲池中的数据成块的发送,若数据的长度不满足缓冲池中的大小,需要继续存入,待数据满足预存大小后再成块的发送。往往在发送文件过程中,文件末尾的数据大小不能满足缓冲池的大小。最终导致这部分的数据停留在缓冲池无法发送。

总而言之,需要我们在write()方法后,手动调用flush()方法,强制刷出缓冲池中的数据,(即使数据长度不满足缓冲池的大小)从而保证数据的正常发送。当然,当我们调用流的close()方法后,系统也会自动将输出流缓冲区的数据刷出,同时可以保证流的物理资源被回收。

2.5 导出Excel应用实例

在2.3中我们寥寥几行代码实际上就已经就是实现了导出Excel一个简单示例,下面我们在看如何实现导出如图所示的Excel表格?
Excel和XML的相互转换(JAVA语言)

package TEST;

import static org.apache.poi.hssf.usermodel.HeaderFooter.fontSize;

import java.io.FileOutputStream;
import java.util.Calendar;
import java.util.Date;

import org.apache.poi.hssf.usermodel.HSSFFooter;
import org.apache.poi.hssf.usermodel.HSSFHeader;
import org.apache.poi.hssf.usermodel.HSSFPrintSetup;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;

public class test {
	
	public static void main(String[] args) throws Exception {
		
		//创建HSSFWorkbook对象
		Workbook wb = new HSSFWorkbook();
		//创建HSSFSheet对象
		Sheet sheet = wb.createSheet("成绩单");
		//创建HSSFRow对象
		Row row = sheet.createRow(0);
		//创建HSSFCell对象
		Cell cell=row.createCell(0);
		//设置单元格的值
		cell.setCellValue("单元格中的中文");
		
		//合并单元格CellRangeAddress 构造参数依次表示起始行,截至行,起始列, 截至列
		sheet.addMergedRegion(new CellRangeAddress(0,0,0,3));
		
		//在sheet里创建第二行
		Row row2=sheet.createRow(1);
		//创建单元格并设置单元格内容
		row2.createCell(0).setCellValue("姓名");
		row2.createCell(1).setCellValue("班级");
		row2.createCell(2).setCellValue("笔试成绩");
		row2.createCell(3).setCellValue("机试成绩");
		
		//在sheet里创建第三行
		Row row3=sheet.createRow(2);
		row3.createCell(0).setCellValue("李明");
		row3.createCell(1).setCellValue("As178");
		row3.createCell(2).setCellValue(87);
		row3.createCell(3).setCellValue(78);
		
		//在sheet里创建第四行
		Row row4=sheet.createRow(3);
		//创建单元格并设置单元格内容
		row4.createCell(0).setCellValue("张飞");
		row4.createCell(1).setCellValue("As255");
		row4.createCell(2).setCellValue(78);
		row4.createCell(3).setCellValue(90);
				
		//在sheet里创建第五行
		Row row5=sheet.createRow(4);
		//创建单元格并设置单元格内容
		row5.createCell(0).setCellValue("王菲");
		row5.createCell(1).setCellValue("As336");
		row5.createCell(2).setCellValue(82);
		row5.createCell(3).setCellValue(69);
		
		//输出Excel文件
		FileOutputStream fileOutStream = new FileOutputStream("D:\\workbook.xls"); 
		wb.write(fileOutStream);
		fileOutStream.flush();
		}
	}

2.6 样式设置

注:考虑到本项目仅用于成绩输出,故仅在此介绍必要的样式设置,包括:
单元格合并、设置单元格的行高和列宽,省略单元格样式、字体样式设置等的内容,如有需要,请自行查阅相关资料。

单元格合并

使用HSSFSheet的addMergedRegion()方法

public int addMergedRegion(CellRangeAddress region)
//构造方法如下
CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol)

设置单元格的行高、列宽

HSSFSheet sheet=wb.createSheet();
 
sheet.setDefaultRowHeightInPoints(10);//设置缺省列高sheet.setDefaultColumnWidth(20);//设置缺省列宽
 
//设置指定列的列宽,256 * 50这种写法是因为width参数单位是单个字符的256分之一
 
sheet.setColumnWidth(cell.getColumnIndex(), 256 * 50);

Excel导入

虽然在项目流程中可能不会用到Excel导入这一步,但是excel处理大量数据是非常方便的,所以我写了这一章。
在此章节中,应该明白数据转换的流程,并且为将xml文件导入Excel做准备。因为方法是封装起来的,所以将xml文件导入Excel只不过是调用其它方法的逆过程。
现在我们假设数据如下:
Excel和XML的相互转换(JAVA语言)
放入Excel中:
不失一般性,我们给出前四行数据
Excel和XML的相互转换(JAVA语言)

考虑数据在java中的中转存储形式,自然我们想到了ArrayList。
因为数据长度往往是不确定的,所以定长数组在这里就不适用了。
ArrayList类似于C++Stl中的Vector,可以在末尾任意增减元素。
Excel和XML的相互转换(JAVA语言)
对于arraylist、vector、linkedlist来说,
vector是线程安全的;
arraylist更适合检索或者在末尾插入删除;
linkedlist适合在中间插入删除。
我们在这里选择arraylist。

同时,想象一个string类型的一维数组,类似地,一个存储字符串的ArrayList对象也只能存储一行数据,因为列数是不确定的。
故,我们用语句:

List<List> outerList=new ArrayList<List>();
List innerList=new ArrayList();

分别建立一个外部List对象和一个内部List对象,外部List对象outerList用于存储innerList对象
这样,就建立起来了一个类似于二维string数组的结构,遍历就变得十分简单了。

一个简洁易懂的读取程序:

package TEST;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;

public class ReadExcel {
    public static void main(String[] args) throws BiffException, IOException {
        ReadExcel obj = new ReadExcel();
        File file = new File("C:/Users/Administrator/Desktop/a/3.xls");
        // 创建输入流,读取Excel,此处file是excel文件
        InputStream is = new FileInputStream(file);
        // 提供的Workbook类
        Workbook wb = Workbook.getWorkbook(is);
        // Excel的页签数量  
        int sheet_size = wb.getNumberOfSheets();
        for (int index = 0; index < sheet_size; index++) {
            List<List> outerList=new ArrayList<List>();
            // 每个页签创建一个Sheet对象
            Sheet sheet = wb.getSheet(index);
            // sheet.getRows()返回该页的总行数
            for (int i = 0; i < sheet.getRows(); i++) {
                List innerList=new ArrayList();
                // sheet.getColumns()返回该页的总列数
                for (int j = 0; j < sheet.getColumns(); j++) {
                    String cellinfo = sheet.getCell(j, i).getContents();
                    if(cellinfo.isEmpty()){
                        continue;
                    }
                    innerList.add(cellinfo);
                }
                outerList.add(i, innerList);
            }
        List excelList = outerList;
        System.out.println("List中数据如下");
        for (int i = 0; i < excelList.size(); i++) {
            List list = (List) excelList.get(i);
            for (int j = 0; j < list.size(); j++) {
                System.out.print(list.get(j));
                System.out.print(" ");
            }
            System.out.println();
        }
        }
    }
}
   

遍历结果如下:

List中数据如下
序号 班级 学号 姓名 作业1 实验1 中文 英文
1 材料类1909 20197203 韩晓敏 54 148
2 材料类1909 20197290 陈杰 50 113
3 材料类1909 20197368 陈瑾 66 200
4 材料类1909
5 材料类1909
6 材料类1909
7 材料类1909
8 材料类1909
9 材料类1909
10 材料类1909
11 材料类1909
12 材料类1909
13 材料类1909
14 材料类1909
15 材料类1909
16 材料类1909
17 材料类1909
18 材料类1909
19 材料类1909
20 材料类1909

注意语句:

outerList.add(i, innerList);

用于向outerList对象添加innerList,第一个参数相当于数组下标
正如其名字一样,ArrayList类和数组是极为相似的。

XML文件导入到Excel

理解了这些数据的存储方式之后,就可以将xml文件导入了。
很自然地,我们想到,能不能将xml作为txt文件读入呢?
这样就能节省很多包的调用,也不用使用dom4j的方法了。
我们不妨假设,成绩是这样:
Excel和XML的相互转换(JAVA语言)
保存在一个xml文件里的。
我们借助BufferedReader中的readLine方法(返回值是一行的string)
tips:
1.string replace方法返回一个新字符串,所以要新建一个string对象接收其返回值
2.BufferedReader是为了提供读的效率而设计的一个包装类,它可以包装字符流。可以从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
3.比较字符串不能用==!要用equals。。。(我是辣鸡)
Java中 == 表示 判断2个变量或对象实例是否指向同一个内存空间,equals()表示 判断2个变量或对象实例所指向的内存空间的值是否相同。

outerList.add(num++,innerList);
innerList.clear();

注意clear()方法,我在调用之后,outerList也跟着空了,所以我猜测:
string类中的add方法是添加了一个地址的引用

读取代码如下:

package TEST;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import jxl.read.biff.BiffException;

public class read_txt {
	public static void main(String[] args) throws BiffException, IOException {
	List<List> outerList=new ArrayList<List>();
	List<String> innerList=new ArrayList();
	// 使用ArrayList来存储每行读取到的字符串
	ArrayList<String> arrayList = new ArrayList<>();
	{
	try {
		FileReader fr = new FileReader("C:/Users/Administrator/Desktop/1.xml");
		BufferedReader bf = new BufferedReader(fr);
		String str;
		// 按行读取字符串
		while ((str = bf.readLine()) != null) {
			arrayList.add(str);
		}
		bf.close();
		fr.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
	// 对ArrayList中存储的字符串进行处理
	int length = arrayList.size();
	int num=0;
	for (int i = 0; i < length; i++) {
		String s = arrayList.get(i);
		String tmp =s.replace("	",""); //去除所有空格,包括首尾、中间
		String jud=tmp.substring(0, 4);//截取前四个字符,两个就够,四个好看
		if(jud.equals("<序号>")||jud.equals("<班级>")||jud.equals("<姓名>")||jud.equals("<中文>")||jud.equals("<英文>")){
			String cont="";
			int fir=4,lst=5;
			while(!tmp.substring(fir, lst).equals("<")){
				cont+=tmp.substring(fir, lst);
				fir++;
				lst++;
			}
			innerList.add(cont);
		}
		else if(tmp.length()>=5){
			String jud1=tmp.substring(0, 5);
			if(jud1.equals("<作业1>")||jud1.equals("<实验1>")){
				String cont="";
				int fir=5,lst=6;
				while(!tmp.substring(fir, lst).equals("<")){
					cont+=tmp.substring(fir, lst);
					fir++;
					lst++;
				}
				innerList.add(cont);
			}
			if(jud1.equals("</成绩>")){
				outerList.add(num++,innerList);
				innerList=new ArrayList();
				//innerList.clear();
			}
		}
	}
	System.out.println("List中数据如下");
    for (int i = 0; i < outerList.size(); i++) {
        List<String> list =  outerList.get(i);
        for (int j = 0; j < list.size(); j++) {
            System.out.print(list.get(j));
            System.out.print(" ");
        }
	}
}
}
}
	

读取结果如下:

List中数据如下
1 材料类1909 韩晓敏 54 148 2 材料类1909 陈杰 50 113 3 材料类1909 陈瑾 66 200
原文链接:https://blog.csdn.net/ethan_10/article/details/80335350