poi事件驱动模式_中文乱码问题的解决方法

   接着上一篇博客,我们继续来介绍poi的使用方法,最近的项目里,经常要处理数据量超过五十万条的Excel表格,这里用到poi外接库,poi有两个使用模式。

        1是用户模式,这个模式操作比较简单,也不需要驱动代码(poi包内部本身就自带了全部的指令代码,可以直接使用,关于使用方法,请参看我的另一篇博客:http://blog.csdn.net/pai_daxing/article/details/75123579),但是在实际使用过程中,用户模式的性能并不好,尤其是在处理大型文件的时候(超过十万条数据额Excel表格的时候),更是这样,处理速度和写文件的速度都很慢(大约每向文件写入十万行数据需要将近一个小时的时间。笔者亲测,处理过程中数据量超过40万条以后,java的虚拟机还会很容易报错。例如Java heap space堆栈溢出错误,jvm处理超时错误)。这时候用户模式就不再适用了。

        2是事件驱动模式,这个模式的读入和写出文件的速度都很快,它是借助ExcelXML格式模型来对文件进行读写的,特点是它只对文件读或者写一遍,并不能很好的支持用户对Excel表的重复或跳跃读取。但是这种方法最大的优点就是运行性能好,可以很好的解决用户模式中的数据量较大时的各种错误,但是poi包并没有提供这种模式下的比较完整地驱动代码。需要使用者自己编译,我之前也是看了很多网上的已经编好的驱动代码,基本都可以运行,例如如下这一份驱动代码示例。是笔者已经测试过,确认可以使用的驱动代码。

package poiTools_Class;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
 
import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 
/**
 * Excel超大数据写入,抽象excel2007读入器,先构建.xlsx一张模板,改写模板中的sheet.xml,
 * 使用这种方法 写入.xlsx文件,不需要太大的内存
 * @version 2014-9-2
 */
 abstract class ExcelWriter {
 
private SpreadsheetWriter sw;
 
/**
 * 写入电子表格的主要流程
 *
 * @param fileName
 * @throws Exception
 */
public void process(String fileName) throws Exception {
// 建立工作簿和电子表格对象
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sheet = wb.createSheet("sheet1");
// 持有电子表格数据的xml文件名 例如 /xl/worksheets/sheet1.xml
String sheetRef = sheet.getPackagePart().getPartName().getName();
 
// 保存模板
FileOutputStream os = new FileOutputStream("template.xlsx");
wb.write(os);
os.close();
 
// 生成xml文件
File tmp = File.createTempFile("sheet", ".xml");
Writer fw = new FileWriter(tmp);
sw = new SpreadsheetWriter(fw);
generate();
fw.close();
 
// 使用产生的数据替换模板
File templateFile = new File("template.xlsx");
FileOutputStream out = new FileOutputStream(fileName);
substitute(templateFile, tmp, sheetRef.substring(1), out);
out.close();
// 删除文件之前调用一下垃圾回收器,否则无法删除模板文件
System.gc();
// 删除临时模板文件
if (templateFile.isFile() && templateFile.exists()) {
templateFile.delete();
}
}
 
/**
 * 类使用者应该使用此方法进行写操作
 *
 * @throws Exception
 */
public abstract void generate() throws Exception;
 
public void beginSheet() throws IOException {
sw.beginSheet();
}
 
public void insertRow(int rowNum) throws IOException {
sw.insertRow(rowNum);
}
 
public void createCell(int columnIndex, String value) throws IOException {
sw.createCell(columnIndex, value, -1);
}
 
public void createCell(int columnIndex, double value) throws IOException {
sw.createCell(columnIndex, value, -1);
}
 
public void endRow() throws IOException {
sw.endRow();
}
 
public void endSheet() throws IOException {
sw.endSheet();
}
 
/**
 *
 * @param zipfile
 *            the template file
 * @param tmpfile
 *            the XML file with the sheet data
 * @param entry
 *            the name of the sheet entry to substitute, e.g.
 *            xl/worksheets/sheet1.xml
 * @param out
 *            the stream to write the result to
 */
private static void substitute(File zipfile, File tmpfile, String entry,
OutputStream out) throws IOException {
ZipFile zip = new ZipFile(zipfile);
ZipOutputStream zos = new ZipOutputStream(out);
 
@SuppressWarnings("unchecked")
Enumeration
   
   
    
     en = (Enumeration
    
    
     
     ) zip.entries();
while (en.hasMoreElements()) {
ZipEntry ze = en.nextElement();
if (!ze.getName().equals(entry)) {
zos.putNextEntry(new ZipEntry(ze.getName()));
InputStream is = zip.getInputStream(ze);
copyStream(is, zos);
is.close();
}
}
zos.putNextEntry(new ZipEntry(entry));
InputStream is = new FileInputStream(tmpfile);
copyStream(is, zos);
is.close();
zos.close();
}
 
private static void copyStream(InputStream in, OutputStream out)
throws IOException {
byte[] chunk = new byte[1024];
int count;
while ((count = in.read(chunk)) >= 0) {
out.write(chunk, 0, count);
}
}
 
/**
 * 在写入器中写入电子表格
 *
 */
public static class SpreadsheetWriter {
private final Writer _out;
private int _rownum;
private static String LINE_SEPARATOR = System
.getProperty("line.separator");
 
public SpreadsheetWriter(Writer out) {
_out = out;
}
 
public void beginSheet() throws IOException {
_out.write("
     
     "
+ "
     
     
      
      ");
_out.write("
      
      
       
       " + LINE_SEPARATOR);
}
 
public void endSheet() throws IOException {
_out.write("
      
      ");
_out.write("
     
     ");
}
 
/**
 * 插入新行
 *
 * @param rownum
 *            以0开始
 */
public void insertRow(int rownum) throws IOException {
_out.write("
     
     
      
      " + LINE_SEPARATOR);
this._rownum = rownum;
}
 
/**
 * 插入行结束标志
 */
public void endRow() throws IOException {
_out.write("
     
     " + LINE_SEPARATOR);
}
 
/**
 * 插入新列
 *
 * @param columnIndex
 * @param value
 * @param styleIndex
 * @throws IOException
 */
public void createCell(int columnIndex, String value, int styleIndex)
throws IOException {
String ref = new CellReference(_rownum, columnIndex)
.formatAsString();
_out.write("
     
     
       
       
         " + encoderXML(value) + " 
       
      
      ");
_out.write("
     
     ");
}
 
public void createCell(int columnIndex, String value)
throws IOException {
createCell(columnIndex, value, -1);
}
 
public void createCell(int columnIndex, double value, int styleIndex)
throws IOException {
String ref = new CellReference(_rownum, columnIndex)
.formatAsString();
_out.write("
     
     
       
       " + value + "
      
      ");
_out.write("
     
     ");
}
 
public void createCell(int columnIndex, double value)
throws IOException {
createCell(columnIndex, value, -1);
}
 
public void createCell(int columnIndex, Calendar value, int styleIndex)
throws IOException {
createCell(columnIndex, DateUtil.getExcelDate(value, false),
styleIndex);
}
}
 
// XML Encode
private static final String[] xmlCode = new String[256];
 
static {
// Special characters
xmlCode['\''] = "'";
xmlCode['\"'] = "\""; // double quote
xmlCode['&'] = "&"; // ampersand
xmlCode['<'] = "<"; // lower than
xmlCode['>'] = ">"; // greater than
}
 
/**
 * 
     
     

* Encode the given text into xml. *

* * @param string * the text to encode * @return the encoded string */ public static String encoderXML(String string) { if (string == null) return ""; int n = string.length(); char character; String xmlchar; StringBuffer buffer = new StringBuffer(); // loop over all the characters of the String. for (int i = 0; i < n; i++) { character = string.charAt(i); // the xmlcode of these characters are added to a StringBuffer // one by one try { xmlchar = xmlCode[character]; if (xmlchar == null) { buffer.append(character); } else { buffer.append(xmlCode[character]); } } catch (ArrayIndexOutOfBoundsException aioobe) { buffer.append(character); } } return buffer.toString(); } /** * 测试方法 */ public static void main(String[] args) throws Exception { String file = "D:/导出测试数据.xlsx"; ExcelWriter writer = new ExcelWriter() { public void generate() throws Exception { // 电子表格开始 this.beginSheet(); for (int rownum = 0; rownum < 100; rownum++) { // 插入新行 this.insertRow(rownum); // 建立新单元格,索引值从0开始,表示第一列 this.createCell(0, "第 " + rownum + " 行"); this.createCell(1, 34343.123456789); this.createCell(2, "23.67%"); this.createCell(3, "12:12:23"); this.createCell(4, "2014-10-11 12:12:23"); this.createCell(5, "true"); this.createCell(6, "false"); // 结束行 this.endRow(); } // 电子表格结束 this.endSheet(); } }; writer.process(file); } }

但是却普遍存在一个问题,就是当我们向Excel中写入中文内容的时候,就会变成乱码,

如下图:

 

对此我反复测试,发现是字符编码集的使用错误造成的,我们使用的windows系统普遍使用的是GBK编码格式。但是Excel表格在解释编码的时候,普遍采用UTF-8格式。因此中文会报错,而英文和数字内容却可以正常显示。我找了很多解决方法,这里分享一种已经测试成功的解决方案:

操作如下:

修改编码方式:

新建一个java工程 然后 
eclipse上 
右键工程–>properties–>Resource–>textfileEncoding


 
看到这里的GBK了吧,也许你早就改过了,所以前面运行结果不是GBK。 
试试改成utf-8,再运行程序。是不是发现运行结果变成utf-8了? 
为了统一编码,这里我们通常设置成utf-8 

再运行代码

System.out.println(System.getProperty("file.encoding"));

是不是发现运行结果变成utf-8了? 

在这个项目下运行我们之前写好的驱动代码,你会发现,中文内容的乱码恢复正常了~


 

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值