一: 主要内容
解决 CsvWriter 存 CSV,CSV 文件打开后中文乱码问题
解决 CsvWriter 存 CSV,CSV 文件最后一行总是多一行空行的问题
解决 CsvWriter 存 CSV,CSV 文件不是第一列的时候, 想存入 "" 即空字符串无法存入显示 null 的问题
二: 解决问题前: 需要做的事情
因为网上的 CsvWrite 的 jar 包导入到我们的工程中是 class 文件, 针对上面的问题是无法修改源码的, 但是我们又想用这个工具来操作 CSV, 所以可以在自己的工程中首先 pom 引用这个 jar 包
- <dependency>
- <groupId.NET.sourceforge.javacsv</groupId>
- <artifactId>javacsv</artifactId>
- <version>2.0</version>
- </dependency>
然后我们在自己的工程中创建一个类: CsvWriterExtend 来继承 CsvWriter, 这样我们就能用网上 CsvWriter 这个 jar 中的方法, 还能基于这个去修改代码, 解决上诉的问题
public class CsvWriterExtend extends CsvWriter {}
三: 优化解决 CsvWriter 工具存在的三个问题: 中文乱码, 末尾行多一行空格 (/r), 非第一列空字符串 "" 显示 null 问题
此处废话不多说, 先上 CsvWriterExtend 类的代码, 然后我们在代码中红色标注的地方会有说明, 这段用来解决什么问题的, 说明一下: CsvWriterExtend 代码只是在 CsvWriter 类的基础上做了一些修改
- /**
- * Copyright (C), 2015-2019, XXX 有限公司
- * FileName: CsvWriterExtend
- * Author: YAML
- * Date: 2019/5/17 15:42
- * Description:
- * History:
- * <author> <time> <version> <desc>
- * 俞美玲 2019.5.17 1.0.0 优化 CsvWriter 工具
- */
- package com.test.CSV.tool;
- import com.csvreader.CsvWriter;
- import java.io.*;
- import java.nio.charset.Charset;
- /**
- * @创建人 yumeiling
- * @创建时间 2019/5/17
- * @描述 优化 CsvWriter 工具, 解决了写入 CSV 打开后中文乱码问题, 解决了写入 CSV 最后一行有 / r 换行的问题
- *
- */
- public class CsvWriterExtend extends CsvWriter {
- private PrintWriter outputStream;
- private String fileName;
- private boolean firstColumn;
- private boolean useCustomRecordDelimiter;
- private Charset charset;
- private CsvWriterExtend.UserSettings userSettings;
- private boolean initialized;
- private boolean closed;
- public static final int ESCAPE_MODE_DOUBLED = 1;
- public static final int ESCAPE_MODE_BACKSLASH = 2;
- private String sheetFirstName;
- public CsvWriterExtend(String var1, char var2, Charset var3,String sheetFirstName) {
- super(var1,var2,var3);
- this.outputStream = null;
- this.fileName = null;
- this.firstColumn = true;
- this.useCustomRecordDelimiter = false;
- this.charset = null;
- this.userSettings = new CsvWriterExtend.UserSettings();
- this.initialized = false;
- this.closed = false;
- //1: 这里加了一个 CSV 表头的第一个名字字段, 用来解决第一个问题: 中文乱码问题
- this.sheetFirstName = sheetFirstName;
- if (var1 == null) {
- throw new IllegalArgumentException("Parameter fileName can not be null.");
- } else if (var3 == null) {
- throw new IllegalArgumentException("Parameter charset can not be null.");
- } else {
- this.fileName = var1;
- this.userSettings.Delimiter = var2;
- this.charset = var3;
- }
- }
- public CsvWriterExtend(Writer var1, char var2) {
- super(var1,var2);
- this.outputStream = null;
- this.fileName = null;
- this.firstColumn = true;
- this.useCustomRecordDelimiter = false;
- this.charset = null;
- this.userSettings = new CsvWriterExtend.UserSettings();
- this.initialized = false;
- this.closed = false;
- if (var1 == null) {
- throw new IllegalArgumentException("Parameter outputStream can not be null.");
- } else {
- this.outputStream = new PrintWriter(var1);
- this.userSettings.Delimiter = var2;
- this.initialized = true;
- }
- }
- public CsvWriterExtend(OutputStream var1, char var2, Charset var3) {
- this(new OutputStreamWriter(var1, var3), var2);
- }
- public char getDelimiter() {
- return this.userSettings.Delimiter;
- }
- public void setDelimiter(char var1) {
- this.userSettings.Delimiter = var1;
- }
- public char getRecordDelimiter() {
- return this.userSettings.RecordDelimiter;
- }
- public void setRecordDelimiter(char var1) {
- this.useCustomRecordDelimiter = true;
- this.userSettings.RecordDelimiter = var1;
- }
- public char getTextQualifier() {
- return this.userSettings.TextQualifier;
- }
- public void setTextQualifier(char var1) {
- this.userSettings.TextQualifier = var1;
- }
- public boolean getUseTextQualifier() {
- return this.userSettings.UseTextQualifier;
- }
- public void setUseTextQualifier(boolean var1) {
- this.userSettings.UseTextQualifier = var1;
- }
- public int getEscapeMode() {
- return this.userSettings.EscapeMode;
- }
- public void setEscapeMode(int var1) {
- this.userSettings.EscapeMode = var1;
- }
- public void setComment(char var1) {
- this.userSettings.Comment = var1;
- }
- public char getComment() {
- return this.userSettings.Comment;
- }
- public boolean getForceQualifier() {
- return this.userSettings.ForceQualifier;
- }
- public void setForceQualifier(boolean var1) {
- this.userSettings.ForceQualifier = var1;
- }
- public void write(String var1, boolean var2) throws IOException {
- this.checkClosed();
- this.checkInit();
- if (var1 == null) {
- var1 = "";
- }
- //2: 这里加了一个判断条件, 用来解决第一个问题: 中文乱码问题
- // 加的目的是: 如果是写入 bom 则 bom 后面不追加逗号, 即在 bom 后面和第一个表头前面, 即两者之间不追加逗号, sheetFirstName 为第一个表头的名字根据实际传入
- if (!this.firstColumn && !var1.contentEquals(sheetFirstName) ){
- this.outputStream.write(this.userSettings.Delimiter);
- }
- boolean var3 = this.userSettings.ForceQualifier;
- if (!var2 && var1.length()> 0) {
- var1 = var1.trim();
- }
- if (!var3 && this.userSettings.UseTextQualifier && (var1.indexOf(this.userSettings.TextQualifier)> -1 || var1.indexOf(this.userSettings.Delimiter)> -1 || !this.useCustomRecordDelimiter && (var1.indexOf(10)> -1 || var1.indexOf(13)> -1) || this.useCustomRecordDelimiter && var1.indexOf(this.userSettings.RecordDelimiter)> -1 || this.firstColumn && var1.length()> 0 && var1.charAt(0) == this.userSettings.Comment || this.firstColumn && var1.length() == 0)) {
- var3 = true;
- }
- if (this.userSettings.UseTextQualifier && !var3 && var1.length()> 0 && var2) {
- char var4 = var1.charAt(0);
- if (var4 == '' || var4 =='\t') {
- var3 = true;
- }
- if (!var3 && var1.length()> 1) {
- char var5 = var1.charAt(var1.length() - 1);
- if (var5 == '' || var5 =='\t') {
- var3 = true;
- }
- }
- }
- //3: 这里加了一个 if 语句, 是为了解决第三个问题: CSV"" 显示 null 的问题
- if(!this.firstColumn && var1.length()==0){
- var3=true;
- }
- if (var3) {
- this.outputStream.write(this.userSettings.TextQualifier);
- if (this.userSettings.EscapeMode == 2) {
- var1 = replace(var1, "\\", "\\\\");
- var1 = replace(var1, ""+ this.userSettings.TextQualifier,"\\" + this.userSettings.TextQualifier);
- } else {
- var1 = replace(var1, ""+ this.userSettings.TextQualifier,"" + this.userSettings.TextQualifier + this.userSettings.TextQualifier);
- }
- } else if (this.userSettings.EscapeMode == 2) {
- var1 = replace(var1, "\\", "\\\\");
- var1 = replace(var1, ""+ this.userSettings.Delimiter,"\\" + this.userSettings.Delimiter);
- if (this.useCustomRecordDelimiter) {
- var1 = replace(var1, ""+ this.userSettings.RecordDelimiter,"\\" + this.userSettings.RecordDelimiter);
- } else {
- var1 = replace(var1, "\r", "\\\r");
- var1 = replace(var1, "\n", "\\\n");
- }
- if (this.firstColumn && var1.length()> 0 && var1.charAt(0) == this.userSettings.Comment) {
- if (var1.length()> 1) {
- var1 = "\\" + this.userSettings.Comment + var1.substring(1);
- } else {
- var1 = "\\" + this.userSettings.Comment;
- }
- }
- }
- this.outputStream.write(var1);
- if (var3) {
- this.outputStream.write(this.userSettings.TextQualifier);
- }
- this.firstColumn = false;
- }
- public void write(String var1) throws IOException {
- this.write(var1, false);
- }
- public void writeComment(String var1) throws IOException {
- this.checkClosed();
- this.checkInit();
- this.outputStream.write(this.userSettings.Comment);
- this.outputStream.write(var1);
- if (this.useCustomRecordDelimiter) {
- this.outputStream.write(this.userSettings.RecordDelimiter);
- } else {
- this.outputStream.println();
- }
- this.firstColumn = true;
- }
- public void writeRecord(String[] var1, boolean var2) throws IOException {
- if (var1 != null && var1.length> 0) {
- for(int var3 = 0; var3 <var1.length; ++var3) {
- this.write(var1[var3], var2);
- }
- this.endRecord();
- }
- }
- public void writeRecord(String[] var1) throws IOException {
- this.writeRecord(var1, false);
- }
- public void writeLastRecord(String[] var1) throws IOException {
- this.writeLastRecord(var1, false);
- }
- //4: 这里加了两个方法 writeLastRecord 和 endLastRecord, 用来解决第二个问题: 某尾总是多一行空行的问题
- public void writeLastRecord(String[] var1, boolean var2) throws IOException {
- if (var1 != null && var1.length> 0) {
- for(int var3 = 0; var3 <var1.length; ++var3) {
- this.write(var1[var3], var2);
- }
- this.endLastRecord();
- }
- }
- public void endLastRecord() throws IOException {
- this.checkClosed();
- this.checkInit();
- if (this.useCustomRecordDelimiter) {
- this.outputStream.write(this.userSettings.RecordDelimiter);
- } else {// 主要在下面这一行, 当执行这个方法来结尾的时候是不追加换行符的
- this.outputStream.print("");
- }
- this.firstColumn = true;
- }
- public void endRecord() throws IOException {
- this.checkClosed();
- this.checkInit();
- if (this.useCustomRecordDelimiter) {
- this.outputStream.write(this.userSettings.RecordDelimiter);
- } else {
- this.outputStream.println();
- }
- this.firstColumn = true;
- }
- private void checkInit() throws IOException {
- if (!this.initialized) {
- if (this.fileName != null) {
- this.outputStream = new PrintWriter(new OutputStreamWriter(new FileOutputStream(this.fileName), this.charset));
- }
- this.initialized = true;
- }
- }
- public void flush() {
- this.outputStream.flush();
- }
- public void close() {
- if (!this.closed) {
- this.close(true);
- this.closed = true;
- }
- }
- private void close(boolean var1) {
- if (!this.closed) {
- if (var1) {
- this.charset = null;
- }
- try {
- if (this.initialized) {
- this.outputStream.close();
- }
- } catch (Exception var3) {
- ;
- }
- this.outputStream = null;
- this.closed = true;
- }
- }
- private void checkClosed() throws IOException {
- if (this.closed) {
- throw new IOException("This instance of the CsvWriter class has already been closed.");
- }
- }
- protected void finalize() {
- this.close(false);
- }
- public static String replace(String var0, String var1, String var2) {
- int var3 = var1.length();
- int var4 = var0.indexOf(var1);
- if (var4 <= -1) {
- return var0;
- } else {
- StringBuffer var5 = new StringBuffer();
- int var6;
- for(var6 = 0; var4 != -1; var4 = var0.indexOf(var1, var6)) {
- var5.append(var0.substring(var6, var4));
- var5.append(var2);
- var6 = var4 + var3;
- }
- var5.append(var0.substring(var6));
- return var5.toString();
- }
- }
- private class UserSettings {
- public char TextQualifier = '"';
- public boolean UseTextQualifier = true;
- public char Delimiter = ',';
- public char RecordDelimiter = 0;
- public char Comment = '#';
- public int EscapeMode = 1;
- public boolean ForceQualifier = false;
- public UserSettings() {
- }
- }
- private class Letters {
- public static final char LF = '\n';
- public static final char CR = '\r';
- public static final char QUOTE = '"';
- public static final char COMMA = ',';
- public static final char SPACE = ' ';
- public static final char TAB = '\t';
- public static final char POUND = '#';
- public static final char BACKSLASH = '\\';
- public static final char NULL = '\u0000';
- private Letters() {
- }
- }
- }
四: 调用写好的 CsvWriterExtend 类, 实现写 CSV 功能
下面给出写 CSV 的方法, 如下红色部分是解决上诉三个问题的关键: 中文乱码, 末尾行多一行空格 (/r), 非第一列空字符串 "" 显示 null 问题
- /**
- * 写 CSV 方法
- */
- public static <T> void writeCSV(Collection<T> dataset, String csvFilePath, String[] csvHeaders) {
- try {
- // 集合长度, 和循环次数, 当循环到最后一条记录时不在末尾插入换行符
- int datasetLength = dataset.size();
- int loop=1;
- // 定义路径, 分隔符, 编码 d, 第一个表头名称
- // 如果是写入 bom 则 bom 后面不追加逗号, 即在 bom 后面和第一个表头前面, 即两者之间不追加逗号, sheetFirstName 为第一个表头的名字根据实际传入
- CsvWriterExtend csvWriter = new CsvWriterExtend(csvFilePath, ',', Charset.forName("UTF-8"),"username"); // 写表头
- // 如果是写入 bom 解决文件乱码, 则不在 bom 后面追加, 号分隔符
- csvWriter.write("\ufeff");
- csvWriter.writeRecord(csvHeaders); // 写内容
- // 遍历集合
- Iterator<T> it = dataset.iterator();
- while (it.hasNext()) {
- T t = (T) it.next();
- // 获取类属性
- Field[] fields = t.getClass().getDeclaredFields();
- String[] csvContent=new String[fields.length];
- for (short i = 0; i <fields.length; i++) {
- Field field = fields[i];
- String fieldName = field.getName();
- String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
- try {
- Class tCls = t.getClass();
- Method getMethod = tCls.getMethod(getMethodName,new Class[] {});
- Object value = getMethod.invoke(t, new Object[] {});
- if (value == null) {
- continue;
- }
- // 取值并赋给数组
- String textvalue=value.toString();
- csvContent[i]=textvalue;
- }catch (Exception e) {
- e.getStackTrace();
- }
- }
- if(loop!=datasetLength){
- // 迭代插入记录
- csvWriter.writeRecord(csvContent);
- }else{
- // 插入最后一条记录
- csvWriter.writeLastRecord(csvContent);
- }
- loop=loop+1;
- for(String csvs:csvContent) {
- System.out.println("记录数据:" + csvs);
- }
- } csvWriter.close();
- System.out.println("<--------CSV 文件写入成功 -------->");
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
调用 CSV 方法即执行类
- public static void main(String[] args) throws Exception{
- // 造测试数据
- List<DataEntity> data = CreateDataModel.createUserData();
- String csvFilePath = "E://data.csv";
- // 表头名称
- // 注册用户名, 注册密码, 登录用户名, 登录密码, 记住我, 邮箱, 分类名称, 文章标题, 文章路径, 文章标签, 文章内容, 评论文章, 期望结果
- String[] csvHeaders = { "username", "password", "loginusername","loginpassword","remeber","email","cname","title","slug","tags","content","comment","expectresult" };
- CreateDataModel.writeCSV(data,csvFilePath,csvHeaders);
- }
生成 CSV 的效果如图:
四: 备注
如果想看更详细的代码, 可参考我的 GitHub 地址, 如上 CSV 的生成已全部上传 GitHub 中, 地址如下:
https://github.com/mmkxyu/autotest.git
来源: http://www.bubuko.com/infodetail-3063157.html