Excel和XML的相互转换(JAVA语言)
文章目录
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表格?
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中:
不失一般性,我们给出前四行数据
考虑数据在java中的中转存储形式,自然我们想到了ArrayList。
因为数据长度往往是不确定的,所以定长数组在这里就不适用了。
ArrayList类似于C++Stl中的Vector,可以在末尾任意增减元素。
对于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的方法了。
我们不妨假设,成绩是这样:
保存在一个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
上一篇: java实现小猫钓鱼游戏