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

来源:互联网 发布:迪达软件 编辑:程序博客网 时间:2024/06/10 15:35

   接着上一篇博客,我们继续来介绍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.xmlString 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("");_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("");_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 Encodeprivate static final String[] xmlCode = new String[256]; static {// Special charactersxmlCode['\''] = "'";xmlCode['\"'] = "\""; // double quotexmlCode['&'] = "&"; // ampersandxmlCode['<'] = "<"; // lower thanxmlCode['>'] = ">"; // 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 onetry {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了? 

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